JsonCpp project page Classes Namespace JsonCpp home page

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