JsonCpp project page JsonCpp home page

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