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