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