JsonCpp project page Classes Namespace JsonCpp home page

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