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