JsonCpp project page JsonCpp home page

json_writer.cpp
Go to the documentation of this file.
1 // Copyright 2011 Baptiste Lepilleur
2 // Distributed under MIT license, or public domain if desired and
3 // recognized in your jurisdiction.
4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5 
6 #if !defined(JSON_IS_AMALGAMATION)
7 #include <json/writer.h>
8 #include "json_tool.h"
9 #endif // if !defined(JSON_IS_AMALGAMATION)
10 #include <iomanip>
11 #include <memory>
12 #include <sstream>
13 #include <utility>
14 #include <set>
15 #include <cassert>
16 #include <cstring>
17 #include <cstdio>
18 
19 #if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
20 #include <float.h>
21 #define isfinite _finite
22 #elif defined(__sun) && defined(__SVR4) //Solaris
23 #include <ieeefp.h>
24 #define isfinite finite
25 #else
26 #include <cmath>
27 #define isfinite std::isfinite
28 #endif
29 
30 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
31 #define snprintf _snprintf
32 #elif defined(__ANDROID__)
33 #define snprintf snprintf
34 #elif __cplusplus >= 201103L
35 #define snprintf std::snprintf
36 #endif
37 
38 #if defined(__BORLANDC__)
39 #include <float.h>
40 #define isfinite _finite
41 #define snprintf _snprintf
42 #endif
43 
44 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
45 // Disable warning about strdup being deprecated.
46 #pragma warning(disable : 4996)
47 #endif
48 
49 namespace Json {
50 
51 typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
52 
53 static bool containsControlCharacter(const char* str) {
54  while (*str) {
55  if (isControlCharacter(*(str++)))
56  return true;
57  }
58  return false;
59 }
60 
61 static bool containsControlCharacter0(const char* str, unsigned len) {
62  char const* end = str + len;
63  while (end != str) {
64  if (isControlCharacter(*str) || 0==*str)
65  return true;
66  ++str;
67  }
68  return false;
69 }
70 
71 std::string valueToString(LargestInt value) {
72  UIntToStringBuffer buffer;
73  char* current = buffer + sizeof(buffer);
74  bool isNegative = value < 0;
75  if (isNegative)
76  value = -value;
77  uintToString(LargestUInt(value), current);
78  if (isNegative)
79  *--current = '-';
80  assert(current >= buffer);
81  return current;
82 }
83 
84 std::string valueToString(LargestUInt value) {
85  UIntToStringBuffer buffer;
86  char* current = buffer + sizeof(buffer);
87  uintToString(value, current);
88  assert(current >= buffer);
89  return current;
90 }
91 
92 #if defined(JSON_HAS_INT64)
93 
94 std::string valueToString(Int value) {
95  return valueToString(LargestInt(value));
96 }
97 
98 std::string valueToString(UInt value) {
99  return valueToString(LargestUInt(value));
100 }
101 
102 #endif // # if defined(JSON_HAS_INT64)
103 
104 std::string valueToString(double value) {
105  // Allocate a buffer that is more than large enough to store the 16 digits of
106  // precision requested below.
107  char buffer[32];
108  int len = -1;
109 
110 // Print into the buffer. We need not request the alternative representation
111 // that always has a decimal point because JSON doesn't distingish the
112 // concepts of reals and integers.
113 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
114  // visual studio 2005 to
115  // avoid warning.
116 #if defined(WINCE)
117  len = _snprintf(buffer, sizeof(buffer), "%.17g", value);
118 #else
119  len = sprintf_s(buffer, sizeof(buffer), "%.17g", value);
120 #endif
121 #else
122  if (isfinite(value)) {
123  len = snprintf(buffer, sizeof(buffer), "%.17g", value);
124  } else {
125  // IEEE standard states that NaN values will not compare to themselves
126  if (value != value) {
127  len = snprintf(buffer, sizeof(buffer), "null");
128  } else if (value < 0) {
129  len = snprintf(buffer, sizeof(buffer), "-1e+9999");
130  } else {
131  len = snprintf(buffer, sizeof(buffer), "1e+9999");
132  }
133  // For those, we do not need to call fixNumLoc, but it is fast.
134  }
135 #endif
136  assert(len >= 0);
137  fixNumericLocale(buffer, buffer + len);
138  return buffer;
139 }
140 
141 std::string valueToString(bool value) { return value ? "true" : "false"; }
142 
143 std::string valueToQuotedString(const char* value) {
144  if (value == NULL)
145  return "";
146  // Not sure how to handle unicode...
147  if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
148  !containsControlCharacter(value))
149  return std::string("\"") + value + "\"";
150  // We have to walk value and escape any special characters.
151  // Appending to std::string is not efficient, but this should be rare.
152  // (Note: forward slashes are *not* rare, but I am not escaping them.)
153  std::string::size_type maxsize =
154  strlen(value) * 2 + 3; // allescaped+quotes+NULL
155  std::string result;
156  result.reserve(maxsize); // to avoid lots of mallocs
157  result += "\"";
158  for (const char* c = value; *c != 0; ++c) {
159  switch (*c) {
160  case '\"':
161  result += "\\\"";
162  break;
163  case '\\':
164  result += "\\\\";
165  break;
166  case '\b':
167  result += "\\b";
168  break;
169  case '\f':
170  result += "\\f";
171  break;
172  case '\n':
173  result += "\\n";
174  break;
175  case '\r':
176  result += "\\r";
177  break;
178  case '\t':
179  result += "\\t";
180  break;
181  // case '/':
182  // Even though \/ is considered a legal escape in JSON, a bare
183  // slash is also legal, so I see no reason to escape it.
184  // (I hope I am not misunderstanding something.
185  // blep notes: actually escaping \/ may be useful in javascript to avoid </
186  // sequence.
187  // Should add a flag to allow this compatibility mode and prevent this
188  // sequence from occurring.
189  default:
190  if (isControlCharacter(*c)) {
191  std::ostringstream oss;
192  oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
193  << std::setw(4) << static_cast<int>(*c);
194  result += oss.str();
195  } else {
196  result += *c;
197  }
198  break;
199  }
200  }
201  result += "\"";
202  return result;
203 }
204 
205 // https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
206 static char const* strnpbrk(char const* s, char const* accept, size_t n) {
207  assert((s || !n) && accept);
208 
209  char const* const end = s + n;
210  for (char const* cur = s; cur < end; ++cur) {
211  int const c = *cur;
212  for (char const* a = accept; *a; ++a) {
213  if (*a == c) {
214  return cur;
215  }
216  }
217  }
218  return NULL;
219 }
220 static std::string valueToQuotedStringN(const char* value, unsigned length) {
221  if (value == NULL)
222  return "";
223  // Not sure how to handle unicode...
224  if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
225  !containsControlCharacter0(value, length))
226  return std::string("\"") + value + "\"";
227  // We have to walk value and escape any special characters.
228  // Appending to std::string is not efficient, but this should be rare.
229  // (Note: forward slashes are *not* rare, but I am not escaping them.)
230  std::string::size_type maxsize =
231  length * 2 + 3; // allescaped+quotes+NULL
232  std::string result;
233  result.reserve(maxsize); // to avoid lots of mallocs
234  result += "\"";
235  char const* end = value + length;
236  for (const char* c = value; c != end; ++c) {
237  switch (*c) {
238  case '\"':
239  result += "\\\"";
240  break;
241  case '\\':
242  result += "\\\\";
243  break;
244  case '\b':
245  result += "\\b";
246  break;
247  case '\f':
248  result += "\\f";
249  break;
250  case '\n':
251  result += "\\n";
252  break;
253  case '\r':
254  result += "\\r";
255  break;
256  case '\t':
257  result += "\\t";
258  break;
259  // case '/':
260  // Even though \/ is considered a legal escape in JSON, a bare
261  // slash is also legal, so I see no reason to escape it.
262  // (I hope I am not misunderstanding something.)
263  // blep notes: actually escaping \/ may be useful in javascript to avoid </
264  // sequence.
265  // Should add a flag to allow this compatibility mode and prevent this
266  // sequence from occurring.
267  default:
268  if ((isControlCharacter(*c)) || (*c == 0)) {
269  std::ostringstream oss;
270  oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
271  << std::setw(4) << static_cast<int>(*c);
272  result += oss.str();
273  } else {
274  result += *c;
275  }
276  break;
277  }
278  }
279  result += "\"";
280  return result;
281 }
282 
283 // Class Writer
284 // //////////////////////////////////////////////////////////////////
286 
287 // Class FastWriter
288 // //////////////////////////////////////////////////////////////////
289 
291  : yamlCompatiblityEnabled_(false) {}
292 
293 void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
294 
295 std::string FastWriter::write(const Value& root) {
296  document_ = "";
297  writeValue(root);
298  document_ += "\n";
299  return document_;
300 }
301 
302 void FastWriter::writeValue(const Value& value) {
303  switch (value.type()) {
304  case nullValue:
305  document_ += "null";
306  break;
307  case intValue:
308  document_ += valueToString(value.asLargestInt());
309  break;
310  case uintValue:
311  document_ += valueToString(value.asLargestUInt());
312  break;
313  case realValue:
314  document_ += valueToString(value.asDouble());
315  break;
316  case stringValue:
317  {
318  // Is NULL possible for value.string_?
319  char const* str;
320  char const* end;
321  bool ok = value.getString(&str, &end);
322  if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
323  break;
324  }
325  case booleanValue:
326  document_ += valueToString(value.asBool());
327  break;
328  case arrayValue: {
329  document_ += '[';
330  int size = value.size();
331  for (int index = 0; index < size; ++index) {
332  if (index > 0)
333  document_ += ',';
334  writeValue(value[index]);
335  }
336  document_ += ']';
337  } break;
338  case objectValue: {
339  Value::Members members(value.getMemberNames());
340  document_ += '{';
341  for (Value::Members::iterator it = members.begin(); it != members.end();
342  ++it) {
343  const std::string& name = *it;
344  if (it != members.begin())
345  document_ += ',';
346  document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
347  document_ += yamlCompatiblityEnabled_ ? ": " : ":";
348  writeValue(value[name]);
349  }
350  document_ += '}';
351  } break;
352  }
353 }
354 
355 // Class StyledWriter
356 // //////////////////////////////////////////////////////////////////
357 
359  : rightMargin_(74), indentSize_(3), addChildValues_() {}
360 
361 std::string StyledWriter::write(const Value& root) {
362  document_ = "";
363  addChildValues_ = false;
364  indentString_ = "";
365  writeCommentBeforeValue(root);
366  writeValue(root);
367  writeCommentAfterValueOnSameLine(root);
368  document_ += "\n";
369  return document_;
370 }
371 
372 void StyledWriter::writeValue(const Value& value) {
373  switch (value.type()) {
374  case nullValue:
375  pushValue("null");
376  break;
377  case intValue:
378  pushValue(valueToString(value.asLargestInt()));
379  break;
380  case uintValue:
381  pushValue(valueToString(value.asLargestUInt()));
382  break;
383  case realValue:
384  pushValue(valueToString(value.asDouble()));
385  break;
386  case stringValue:
387  {
388  // Is NULL possible for value.string_?
389  char const* str;
390  char const* end;
391  bool ok = value.getString(&str, &end);
392  if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
393  else pushValue("");
394  break;
395  }
396  case booleanValue:
397  pushValue(valueToString(value.asBool()));
398  break;
399  case arrayValue:
400  writeArrayValue(value);
401  break;
402  case objectValue: {
403  Value::Members members(value.getMemberNames());
404  if (members.empty())
405  pushValue("{}");
406  else {
407  writeWithIndent("{");
408  indent();
409  Value::Members::iterator it = members.begin();
410  for (;;) {
411  const std::string& name = *it;
412  const Value& childValue = value[name];
413  writeCommentBeforeValue(childValue);
414  writeWithIndent(valueToQuotedString(name.c_str()));
415  document_ += " : ";
416  writeValue(childValue);
417  if (++it == members.end()) {
418  writeCommentAfterValueOnSameLine(childValue);
419  break;
420  }
421  document_ += ',';
422  writeCommentAfterValueOnSameLine(childValue);
423  }
424  unindent();
425  writeWithIndent("}");
426  }
427  } break;
428  }
429 }
430 
431 void StyledWriter::writeArrayValue(const Value& value) {
432  unsigned size = value.size();
433  if (size == 0)
434  pushValue("[]");
435  else {
436  bool isArrayMultiLine = isMultineArray(value);
437  if (isArrayMultiLine) {
438  writeWithIndent("[");
439  indent();
440  bool hasChildValue = !childValues_.empty();
441  unsigned index = 0;
442  for (;;) {
443  const Value& childValue = value[index];
444  writeCommentBeforeValue(childValue);
445  if (hasChildValue)
446  writeWithIndent(childValues_[index]);
447  else {
448  writeIndent();
449  writeValue(childValue);
450  }
451  if (++index == size) {
452  writeCommentAfterValueOnSameLine(childValue);
453  break;
454  }
455  document_ += ',';
456  writeCommentAfterValueOnSameLine(childValue);
457  }
458  unindent();
459  writeWithIndent("]");
460  } else // output on a single line
461  {
462  assert(childValues_.size() == size);
463  document_ += "[ ";
464  for (unsigned index = 0; index < size; ++index) {
465  if (index > 0)
466  document_ += ", ";
467  document_ += childValues_[index];
468  }
469  document_ += " ]";
470  }
471  }
472 }
473 
474 bool StyledWriter::isMultineArray(const Value& value) {
475  int size = value.size();
476  bool isMultiLine = size * 3 >= rightMargin_;
477  childValues_.clear();
478  for (int index = 0; index < size && !isMultiLine; ++index) {
479  const Value& childValue = value[index];
480  isMultiLine =
481  isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
482  childValue.size() > 0);
483  }
484  if (!isMultiLine) // check if line length > max line length
485  {
486  childValues_.reserve(size);
487  addChildValues_ = true;
488  int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
489  for (int index = 0; index < size; ++index) {
490  if (hasCommentForValue(value[index])) {
491  isMultiLine = true;
492  }
493  writeValue(value[index]);
494  lineLength += int(childValues_[index].length());
495  }
496  addChildValues_ = false;
497  isMultiLine = isMultiLine || lineLength >= rightMargin_;
498  }
499  return isMultiLine;
500 }
501 
502 void StyledWriter::pushValue(const std::string& value) {
503  if (addChildValues_)
504  childValues_.push_back(value);
505  else
506  document_ += value;
507 }
508 
509 void StyledWriter::writeIndent() {
510  if (!document_.empty()) {
511  char last = document_[document_.length() - 1];
512  if (last == ' ') // already indented
513  return;
514  if (last != '\n') // Comments may add new-line
515  document_ += '\n';
516  }
517  document_ += indentString_;
518 }
519 
520 void StyledWriter::writeWithIndent(const std::string& value) {
521  writeIndent();
522  document_ += value;
523 }
524 
525 void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
526 
527 void StyledWriter::unindent() {
528  assert(int(indentString_.size()) >= indentSize_);
529  indentString_.resize(indentString_.size() - indentSize_);
530 }
531 
532 void StyledWriter::writeCommentBeforeValue(const Value& root) {
533  if (!root.hasComment(commentBefore))
534  return;
535 
536  document_ += "\n";
537  writeIndent();
538  const std::string& comment = root.getComment(commentBefore);
539  std::string::const_iterator iter = comment.begin();
540  while (iter != comment.end()) {
541  document_ += *iter;
542  if (*iter == '\n' &&
543  (iter != comment.end() && *(iter + 1) == '/'))
544  writeIndent();
545  ++iter;
546  }
547 
548  // Comments are stripped of trailing newlines, so add one here
549  document_ += "\n";
550 }
551 
552 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
554  document_ += " " + root.getComment(commentAfterOnSameLine);
555 
556  if (root.hasComment(commentAfter)) {
557  document_ += "\n";
558  document_ += root.getComment(commentAfter);
559  document_ += "\n";
560  }
561 }
562 
563 bool StyledWriter::hasCommentForValue(const Value& value) {
564  return value.hasComment(commentBefore) ||
566  value.hasComment(commentAfter);
567 }
568 
569 // Class StyledStreamWriter
570 // //////////////////////////////////////////////////////////////////
571 
573  : document_(NULL), rightMargin_(74), indentation_(indentation),
574  addChildValues_() {}
575 
576 void StyledStreamWriter::write(std::ostream& out, const Value& root) {
577  document_ = &out;
578  addChildValues_ = false;
579  indentString_ = "";
580  indented_ = true;
581  writeCommentBeforeValue(root);
582  if (!indented_) writeIndent();
583  indented_ = true;
584  writeValue(root);
585  writeCommentAfterValueOnSameLine(root);
586  *document_ << "\n";
587  document_ = NULL; // Forget the stream, for safety.
588 }
589 
590 void StyledStreamWriter::writeValue(const Value& value) {
591  switch (value.type()) {
592  case nullValue:
593  pushValue("null");
594  break;
595  case intValue:
596  pushValue(valueToString(value.asLargestInt()));
597  break;
598  case uintValue:
599  pushValue(valueToString(value.asLargestUInt()));
600  break;
601  case realValue:
602  pushValue(valueToString(value.asDouble()));
603  break;
604  case stringValue:
605  {
606  // Is NULL possible for value.string_?
607  char const* str;
608  char const* end;
609  bool ok = value.getString(&str, &end);
610  if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
611  else pushValue("");
612  break;
613  }
614  case booleanValue:
615  pushValue(valueToString(value.asBool()));
616  break;
617  case arrayValue:
618  writeArrayValue(value);
619  break;
620  case objectValue: {
621  Value::Members members(value.getMemberNames());
622  if (members.empty())
623  pushValue("{}");
624  else {
625  writeWithIndent("{");
626  indent();
627  Value::Members::iterator it = members.begin();
628  for (;;) {
629  const std::string& name = *it;
630  const Value& childValue = value[name];
631  writeCommentBeforeValue(childValue);
632  writeWithIndent(valueToQuotedString(name.c_str()));
633  *document_ << " : ";
634  writeValue(childValue);
635  if (++it == members.end()) {
636  writeCommentAfterValueOnSameLine(childValue);
637  break;
638  }
639  *document_ << ",";
640  writeCommentAfterValueOnSameLine(childValue);
641  }
642  unindent();
643  writeWithIndent("}");
644  }
645  } break;
646  }
647 }
648 
649 void StyledStreamWriter::writeArrayValue(const Value& value) {
650  unsigned size = value.size();
651  if (size == 0)
652  pushValue("[]");
653  else {
654  bool isArrayMultiLine = isMultineArray(value);
655  if (isArrayMultiLine) {
656  writeWithIndent("[");
657  indent();
658  bool hasChildValue = !childValues_.empty();
659  unsigned index = 0;
660  for (;;) {
661  const Value& childValue = value[index];
662  writeCommentBeforeValue(childValue);
663  if (hasChildValue)
664  writeWithIndent(childValues_[index]);
665  else {
666  if (!indented_) writeIndent();
667  indented_ = true;
668  writeValue(childValue);
669  indented_ = false;
670  }
671  if (++index == size) {
672  writeCommentAfterValueOnSameLine(childValue);
673  break;
674  }
675  *document_ << ",";
676  writeCommentAfterValueOnSameLine(childValue);
677  }
678  unindent();
679  writeWithIndent("]");
680  } else // output on a single line
681  {
682  assert(childValues_.size() == size);
683  *document_ << "[ ";
684  for (unsigned index = 0; index < size; ++index) {
685  if (index > 0)
686  *document_ << ", ";
687  *document_ << childValues_[index];
688  }
689  *document_ << " ]";
690  }
691  }
692 }
693 
694 bool StyledStreamWriter::isMultineArray(const Value& value) {
695  int size = value.size();
696  bool isMultiLine = size * 3 >= rightMargin_;
697  childValues_.clear();
698  for (int index = 0; index < size && !isMultiLine; ++index) {
699  const Value& childValue = value[index];
700  isMultiLine =
701  isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
702  childValue.size() > 0);
703  }
704  if (!isMultiLine) // check if line length > max line length
705  {
706  childValues_.reserve(size);
707  addChildValues_ = true;
708  int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
709  for (int index = 0; index < size; ++index) {
710  if (hasCommentForValue(value[index])) {
711  isMultiLine = true;
712  }
713  writeValue(value[index]);
714  lineLength += int(childValues_[index].length());
715  }
716  addChildValues_ = false;
717  isMultiLine = isMultiLine || lineLength >= rightMargin_;
718  }
719  return isMultiLine;
720 }
721 
722 void StyledStreamWriter::pushValue(const std::string& value) {
723  if (addChildValues_)
724  childValues_.push_back(value);
725  else
726  *document_ << value;
727 }
728 
729 void StyledStreamWriter::writeIndent() {
730  // blep intended this to look at the so-far-written string
731  // to determine whether we are already indented, but
732  // with a stream we cannot do that. So we rely on some saved state.
733  // The caller checks indented_.
734  *document_ << '\n' << indentString_;
735 }
736 
737 void StyledStreamWriter::writeWithIndent(const std::string& value) {
738  if (!indented_) writeIndent();
739  *document_ << value;
740  indented_ = false;
741 }
742 
743 void StyledStreamWriter::indent() { indentString_ += indentation_; }
744 
745 void StyledStreamWriter::unindent() {
746  assert(indentString_.size() >= indentation_.size());
747  indentString_.resize(indentString_.size() - indentation_.size());
748 }
749 
750 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
751  if (!root.hasComment(commentBefore))
752  return;
753 
754  if (!indented_) writeIndent();
755  const std::string& comment = root.getComment(commentBefore);
756  std::string::const_iterator iter = comment.begin();
757  while (iter != comment.end()) {
758  *document_ << *iter;
759  if (*iter == '\n' &&
760  (iter != comment.end() && *(iter + 1) == '/'))
761  // writeIndent(); // would include newline
762  *document_ << indentString_;
763  ++iter;
764  }
765  indented_ = false;
766 }
767 
768 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
770  *document_ << ' ' << root.getComment(commentAfterOnSameLine);
771 
772  if (root.hasComment(commentAfter)) {
773  writeIndent();
774  *document_ << root.getComment(commentAfter);
775  }
776  indented_ = false;
777 }
778 
779 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
780  return value.hasComment(commentBefore) ||
782  value.hasComment(commentAfter);
783 }
784 
786 // BuiltStyledStreamWriter
787 
789 struct CommentStyle {
791  enum Enum {
792  None,
793  Most,
794  All
795  };
796 };
797 
798 struct BuiltStyledStreamWriter : public StreamWriter
799 {
800  BuiltStyledStreamWriter(
801  std::string const& indentation,
802  CommentStyle::Enum cs,
803  std::string const& colonSymbol,
804  std::string const& nullSymbol,
805  std::string const& endingLineFeedSymbol);
806  virtual int write(Value const& root, std::ostream* sout);
807 private:
808  void writeValue(Value const& value);
809  void writeArrayValue(Value const& value);
810  bool isMultineArray(Value const& value);
811  void pushValue(std::string const& value);
812  void writeIndent();
813  void writeWithIndent(std::string const& value);
814  void indent();
815  void unindent();
816  void writeCommentBeforeValue(Value const& root);
817  void writeCommentAfterValueOnSameLine(Value const& root);
818  static bool hasCommentForValue(const Value& value);
819 
820  typedef std::vector<std::string> ChildValues;
821 
822  ChildValues childValues_;
823  std::string indentString_;
824  int rightMargin_;
825  std::string indentation_;
826  CommentStyle::Enum cs_;
827  std::string colonSymbol_;
828  std::string nullSymbol_;
829  std::string endingLineFeedSymbol_;
830  bool addChildValues_ : 1;
831  bool indented_ : 1;
832 };
833 BuiltStyledStreamWriter::BuiltStyledStreamWriter(
834  std::string const& indentation,
835  CommentStyle::Enum cs,
836  std::string const& colonSymbol,
837  std::string const& nullSymbol,
838  std::string const& endingLineFeedSymbol)
839  : rightMargin_(74)
840  , indentation_(indentation)
841  , cs_(cs)
842  , colonSymbol_(colonSymbol)
843  , nullSymbol_(nullSymbol)
844  , endingLineFeedSymbol_(endingLineFeedSymbol)
845  , addChildValues_(false)
846  , indented_(false)
847 {
848 }
849 int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
850 {
851  sout_ = sout;
852  addChildValues_ = false;
853  indented_ = true;
854  indentString_ = "";
855  writeCommentBeforeValue(root);
856  if (!indented_) writeIndent();
857  indented_ = true;
858  writeValue(root);
859  writeCommentAfterValueOnSameLine(root);
860  *sout_ << endingLineFeedSymbol_;
861  sout_ = NULL;
862  return 0;
863 }
864 void BuiltStyledStreamWriter::writeValue(Value const& value) {
865  switch (value.type()) {
866  case nullValue:
867  pushValue(nullSymbol_);
868  break;
869  case intValue:
870  pushValue(valueToString(value.asLargestInt()));
871  break;
872  case uintValue:
873  pushValue(valueToString(value.asLargestUInt()));
874  break;
875  case realValue:
876  pushValue(valueToString(value.asDouble()));
877  break;
878  case stringValue:
879  {
880  // Is NULL is possible for value.string_?
881  char const* str;
882  char const* end;
883  bool ok = value.getString(&str, &end);
884  if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
885  else pushValue("");
886  break;
887  }
888  case booleanValue:
889  pushValue(valueToString(value.asBool()));
890  break;
891  case arrayValue:
892  writeArrayValue(value);
893  break;
894  case objectValue: {
895  Value::Members members(value.getMemberNames());
896  if (members.empty())
897  pushValue("{}");
898  else {
899  writeWithIndent("{");
900  indent();
901  Value::Members::iterator it = members.begin();
902  for (;;) {
903  std::string const& name = *it;
904  Value const& childValue = value[name];
905  writeCommentBeforeValue(childValue);
906  writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));
907  *sout_ << colonSymbol_;
908  writeValue(childValue);
909  if (++it == members.end()) {
910  writeCommentAfterValueOnSameLine(childValue);
911  break;
912  }
913  *sout_ << ",";
914  writeCommentAfterValueOnSameLine(childValue);
915  }
916  unindent();
917  writeWithIndent("}");
918  }
919  } break;
920  }
921 }
922 
923 void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
924  unsigned size = value.size();
925  if (size == 0)
926  pushValue("[]");
927  else {
928  bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
929  if (isMultiLine) {
930  writeWithIndent("[");
931  indent();
932  bool hasChildValue = !childValues_.empty();
933  unsigned index = 0;
934  for (;;) {
935  Value const& childValue = value[index];
936  writeCommentBeforeValue(childValue);
937  if (hasChildValue)
938  writeWithIndent(childValues_[index]);
939  else {
940  if (!indented_) writeIndent();
941  indented_ = true;
942  writeValue(childValue);
943  indented_ = false;
944  }
945  if (++index == size) {
946  writeCommentAfterValueOnSameLine(childValue);
947  break;
948  }
949  *sout_ << ",";
950  writeCommentAfterValueOnSameLine(childValue);
951  }
952  unindent();
953  writeWithIndent("]");
954  } else // output on a single line
955  {
956  assert(childValues_.size() == size);
957  *sout_ << "[";
958  if (!indentation_.empty()) *sout_ << " ";
959  for (unsigned index = 0; index < size; ++index) {
960  if (index > 0)
961  *sout_ << ", ";
962  *sout_ << childValues_[index];
963  }
964  if (!indentation_.empty()) *sout_ << " ";
965  *sout_ << "]";
966  }
967  }
968 }
969 
970 bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
971  int size = value.size();
972  bool isMultiLine = size * 3 >= rightMargin_;
973  childValues_.clear();
974  for (int index = 0; index < size && !isMultiLine; ++index) {
975  Value const& childValue = value[index];
976  isMultiLine =
977  isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
978  childValue.size() > 0);
979  }
980  if (!isMultiLine) // check if line length > max line length
981  {
982  childValues_.reserve(size);
983  addChildValues_ = true;
984  int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
985  for (int index = 0; index < size; ++index) {
986  if (hasCommentForValue(value[index])) {
987  isMultiLine = true;
988  }
989  writeValue(value[index]);
990  lineLength += int(childValues_[index].length());
991  }
992  addChildValues_ = false;
993  isMultiLine = isMultiLine || lineLength >= rightMargin_;
994  }
995  return isMultiLine;
996 }
997 
998 void BuiltStyledStreamWriter::pushValue(std::string const& value) {
999  if (addChildValues_)
1000  childValues_.push_back(value);
1001  else
1002  *sout_ << value;
1003 }
1004 
1005 void BuiltStyledStreamWriter::writeIndent() {
1006  // blep intended this to look at the so-far-written string
1007  // to determine whether we are already indented, but
1008  // with a stream we cannot do that. So we rely on some saved state.
1009  // The caller checks indented_.
1010 
1011  if (!indentation_.empty()) {
1012  // In this case, drop newlines too.
1013  *sout_ << '\n' << indentString_;
1014  }
1015 }
1016 
1017 void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) {
1018  if (!indented_) writeIndent();
1019  *sout_ << value;
1020  indented_ = false;
1021 }
1022 
1023 void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1024 
1025 void BuiltStyledStreamWriter::unindent() {
1026  assert(indentString_.size() >= indentation_.size());
1027  indentString_.resize(indentString_.size() - indentation_.size());
1028 }
1029 
1030 void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
1031  if (cs_ == CommentStyle::None) return;
1032  if (!root.hasComment(commentBefore))
1033  return;
1034 
1035  if (!indented_) writeIndent();
1036  const std::string& comment = root.getComment(commentBefore);
1037  std::string::const_iterator iter = comment.begin();
1038  while (iter != comment.end()) {
1039  *sout_ << *iter;
1040  if (*iter == '\n' &&
1041  (iter != comment.end() && *(iter + 1) == '/'))
1042  // writeIndent(); // would write extra newline
1043  *sout_ << indentString_;
1044  ++iter;
1045  }
1046  indented_ = false;
1047 }
1048 
1049 void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
1050  if (cs_ == CommentStyle::None) return;
1052  *sout_ << " " + root.getComment(commentAfterOnSameLine);
1053 
1054  if (root.hasComment(commentAfter)) {
1055  writeIndent();
1056  *sout_ << root.getComment(commentAfter);
1057  }
1058 }
1059 
1060 // static
1061 bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1062  return value.hasComment(commentBefore) ||
1064  value.hasComment(commentAfter);
1065 }
1066 
1068 // StreamWriter
1069 
1071  : sout_(NULL)
1072 {
1073 }
1075 {
1076 }
1078 {}
1080 {
1081  setDefaults(&settings_);
1082 }
1084 {}
1086 {
1087  std::string indentation = settings_["indentation"].asString();
1088  std::string cs_str = settings_["commentStyle"].asString();
1089  bool eyc = settings_["enableYAMLCompatibility"].asBool();
1090  bool dnp = settings_["dropNullPlaceholders"].asBool();
1091  CommentStyle::Enum cs = CommentStyle::All;
1092  if (cs_str == "All") {
1093  cs = CommentStyle::All;
1094  } else if (cs_str == "None") {
1095  cs = CommentStyle::None;
1096  } else {
1097  throwRuntimeError("commentStyle must be 'All' or 'None'");
1098  }
1099  std::string colonSymbol = " : ";
1100  if (eyc) {
1101  colonSymbol = ": ";
1102  } else if (indentation.empty()) {
1103  colonSymbol = ":";
1104  }
1105  std::string nullSymbol = "null";
1106  if (dnp) {
1107  nullSymbol = "";
1108  }
1109  std::string endingLineFeedSymbol = "";
1110  return new BuiltStyledStreamWriter(
1111  indentation, cs,
1112  colonSymbol, nullSymbol, endingLineFeedSymbol);
1113 }
1114 static void getValidWriterKeys(std::set<std::string>* valid_keys)
1115 {
1116  valid_keys->clear();
1117  valid_keys->insert("indentation");
1118  valid_keys->insert("commentStyle");
1119  valid_keys->insert("enableYAMLCompatibility");
1120  valid_keys->insert("dropNullPlaceholders");
1121 }
1123 {
1124  Json::Value my_invalid;
1125  if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
1126  Json::Value& inv = *invalid;
1127  std::set<std::string> valid_keys;
1128  getValidWriterKeys(&valid_keys);
1129  Value::Members keys = settings_.getMemberNames();
1130  size_t n = keys.size();
1131  for (size_t i = 0; i < n; ++i) {
1132  std::string const& key = keys[i];
1133  if (valid_keys.find(key) == valid_keys.end()) {
1134  inv[key] = settings_[key];
1135  }
1136  }
1137  return 0u == inv.size();
1138 }
1140 {
1141  return settings_[key];
1142 }
1143 // static
1145 {
1147  (*settings)["commentStyle"] = "All";
1148  (*settings)["indentation"] = "\t";
1149  (*settings)["enableYAMLCompatibility"] = false;
1150  (*settings)["dropNullPlaceholders"] = false;
1152 }
1153 
1154 std::string writeString(StreamWriter::Factory const& builder, Value const& root) {
1155  std::ostringstream sout;
1156  StreamWriterPtr const writer(builder.newStreamWriter());
1157  writer->write(root, &sout);
1158  return sout.str();
1159 }
1160 
1161 std::ostream& operator<<(std::ostream& sout, Value const& root) {
1162  StreamWriterBuilder builder;
1163  StreamWriterPtr const writer(builder.newStreamWriter());
1164  writer->write(root, &sout);
1165  return sout;
1166 }
1167 
1168 } // namespace Json
bool hasComment(CommentPlacement placement) const
Value & operator[](std::string key)
A simple way to update a specific setting.
A simple abstract factory.
Definition: writer.h:56
Int64 LargestInt
Definition: config.h:103
#define snprintf
Definition: json_writer.cpp:31
static void uintToString(LargestUInt value, char *&current)
Converts an unsigned integer to string.
Definition: json_tool.h:63
static void setDefaults(Json::Value *settings)
Called by ctor, but you can use this to reset settings_.
std::ostream & operator<<(std::ostream &, const Value &root)
Output using the StyledStreamWriter.
std::vector< std::string > Members
Definition: value.h:165
double asDouble() const
Definition: json_value.cpp:741
array value (ordered list)
Definition: value.h:85
LargestUInt asLargestUInt() const
Definition: json_value.cpp:733
unsigned integer value
Definition: value.h:81
std::string valueToQuotedString(const char *value)
virtual StreamWriter * newStreamWriter() const
object value (collection of name/value pairs).
Definition: value.h:86
virtual std::string write(const Value &root)
void enableYAMLCompatibility()
StyledStreamWriter(std::string indentation="\t")
void write(std::ostream &out, const Value &root)
Serialize a Value in JSON format.
char UIntToStringBuffer[uintToStringBufferSize]
Definition: json_tool.h:56
static bool isControlCharacter(char ch)
Returns true if ch is a control character (in range [1,31]).
Definition: json_tool.h:47
#define isfinite
Definition: json_writer.cpp:21
bool asBool() const
Definition: json_value.cpp:785
static void fixNumericLocale(char *begin, char *end)
Change &#39;,&#39; to &#39;.
Definition: json_tool.h:76
bool isObject() const
static void getValidWriterKeys(std::set< std::string > *valid_keys)
std::string getComment(CommentPlacement placement) const
Include delimiters and embedded newlines.
&#39;null&#39; value
Definition: value.h:79
virtual StreamWriter * newStreamWriter() const =0
Allocate a CharReader via operator new().
UInt64 LargestUInt
Definition: config.h:104
std::string valueToString(Int value)
Definition: json_writer.cpp:94
bool validate(Json::Value *invalid) const
virtual std::string write(const Value &root)
Serialize a Value in JSON format.
JSON (JavaScript Object Notation).
Definition: config.h:87
Members getMemberNames() const
Return a list of the member names.
std::auto_ptr< StreamWriter > StreamWriterPtr
Definition: json_writer.cpp:51
double value
Definition: value.h:82
void throwRuntimeError(std::string const &msg)
used internally
Definition: json_value.cpp:171
static std::string valueToQuotedStringN(const char *value, unsigned length)
virtual ~Writer()
ArrayIndex size() const
Number of values in array or object.
Definition: json_value.cpp:838
Represents a JSON value.
Definition: value.h:162
ValueType type() const
Definition: json_value.cpp:474
static bool containsControlCharacter0(const char *str, unsigned len)
Definition: json_writer.cpp:61
a comment on the line after a value (only make sense for
Definition: value.h:92
LargestInt asLargestInt() const
Definition: json_value.cpp:725
unsigned int UInt
Definition: config.h:89
bool isArray() const
std::string writeString(StreamWriter::Factory const &factory, Value const &root)
Write into stringstream, then return string, for convenience.
static char const * strnpbrk(char const *s, char const *accept, size_t n)
bool value
Definition: value.h:84
static bool containsControlCharacter(const char *str)
Definition: json_writer.cpp:53
signed integer value
Definition: value.h:80
int Int
Definition: config.h:88
a comment placed on the line before a value
Definition: value.h:90
UTF-8 string value.
Definition: value.h:83
a comment just after a value on the same line
Definition: value.h:91
Build a StreamWriter implementation.
Definition: writer.h:87
bool getString(char const **begin, char const **end) const
Get raw char* of string-value.
Definition: json_value.cpp:592