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