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