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