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