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