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