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 <utility>
11 #include <assert.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <sstream>
15 #include <iomanip>
16 #include <math.h>
17 
18 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
19 #include <float.h>
20 #define isfinite _finite
21 #define snprintf _snprintf
22 #endif
23 
24 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
25 // Disable warning about strdup being deprecated.
26 #pragma warning(disable : 4996)
27 #endif
28 
29 #if defined(__sun) && defined(__SVR4) //Solaris
30 #include <ieeefp.h>
31 #define isfinite finite
32 #endif
33 
34 namespace Json {
35 
36 static bool containsControlCharacter(const char* str) {
37  while (*str) {
38  if (isControlCharacter(*(str++)))
39  return true;
40  }
41  return false;
42 }
43 
44 std::string valueToString(LargestInt value) {
45  UIntToStringBuffer buffer;
46  char* current = buffer + sizeof(buffer);
47  bool isNegative = value < 0;
48  if (isNegative)
49  value = -value;
50  uintToString(LargestUInt(value), current);
51  if (isNegative)
52  *--current = '-';
53  assert(current >= buffer);
54  return current;
55 }
56 
57 std::string valueToString(LargestUInt value) {
58  UIntToStringBuffer buffer;
59  char* current = buffer + sizeof(buffer);
60  uintToString(value, current);
61  assert(current >= buffer);
62  return current;
63 }
64 
65 #if defined(JSON_HAS_INT64)
66 
67 std::string valueToString(Int value) {
68  return valueToString(LargestInt(value));
69 }
70 
71 std::string valueToString(UInt value) {
72  return valueToString(LargestUInt(value));
73 }
74 
75 #endif // # if defined(JSON_HAS_INT64)
76 
77 std::string valueToString(double value) {
78  // Allocate a buffer that is more than large enough to store the 16 digits of
79  // precision requested below.
80  char buffer[32];
81  int len = -1;
82 
83 // Print into the buffer. We need not request the alternative representation
84 // that always has a decimal point because JSON doesn't distingish the
85 // concepts of reals and integers.
86 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
87  // visual studio 2005 to
88  // avoid warning.
89 #if defined(WINCE)
90  len = _snprintf(buffer, sizeof(buffer), "%.16g", value);
91 #else
92  len = sprintf_s(buffer, sizeof(buffer), "%.16g", value);
93 #endif
94 #else
95  if (isfinite(value)) {
96  len = snprintf(buffer, sizeof(buffer), "%.16g", value);
97  } else {
98  // IEEE standard states that NaN values will not compare to themselves
99  if (value != value) {
100  len = snprintf(buffer, sizeof(buffer), "null");
101  } else if (value < 0) {
102  len = snprintf(buffer, sizeof(buffer), "-1e+9999");
103  } else {
104  len = snprintf(buffer, sizeof(buffer), "1e+9999");
105  }
106  // For those, we do not need to call fixNumLoc, but it is fast.
107  }
108 #endif
109  assert(len >= 0);
110  fixNumericLocale(buffer, buffer + len);
111  return buffer;
112 }
113 
114 std::string valueToString(bool value) { return value ? "true" : "false"; }
115 
116 std::string valueToQuotedString(const char* value) {
117  if (value == NULL)
118  return "";
119  // Not sure how to handle unicode...
120  if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
121  !containsControlCharacter(value))
122  return std::string("\"") + value + "\"";
123  // We have to walk value and escape any special characters.
124  // Appending to std::string is not efficient, but this should be rare.
125  // (Note: forward slashes are *not* rare, but I am not escaping them.)
126  std::string::size_type maxsize =
127  strlen(value) * 2 + 3; // allescaped+quotes+NULL
128  std::string result;
129  result.reserve(maxsize); // to avoid lots of mallocs
130  result += "\"";
131  for (const char* c = value; *c != 0; ++c) {
132  switch (*c) {
133  case '\"':
134  result += "\\\"";
135  break;
136  case '\\':
137  result += "\\\\";
138  break;
139  case '\b':
140  result += "\\b";
141  break;
142  case '\f':
143  result += "\\f";
144  break;
145  case '\n':
146  result += "\\n";
147  break;
148  case '\r':
149  result += "\\r";
150  break;
151  case '\t':
152  result += "\\t";
153  break;
154  // case '/':
155  // Even though \/ is considered a legal escape in JSON, a bare
156  // slash is also legal, so I see no reason to escape it.
157  // (I hope I am not misunderstanding something.
158  // blep notes: actually escaping \/ may be useful in javascript to avoid </
159  // sequence.
160  // Should add a flag to allow this compatibility mode and prevent this
161  // sequence from occurring.
162  default:
163  if (isControlCharacter(*c)) {
164  std::ostringstream oss;
165  oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
166  << std::setw(4) << static_cast<int>(*c);
167  result += oss.str();
168  } else {
169  result += *c;
170  }
171  break;
172  }
173  }
174  result += "\"";
175  return result;
176 }
177 
178 // Class Writer
179 // //////////////////////////////////////////////////////////////////
181 
182 // Class FastWriter
183 // //////////////////////////////////////////////////////////////////
184 
186  : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
187  omitEndingLineFeed_(false) {}
188 
189 void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
190 
191 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
192 
193 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
194 
195 std::string FastWriter::write(const Value& root) {
196  document_ = "";
197  writeValue(root);
198  if (!omitEndingLineFeed_)
199  document_ += "\n";
200  return document_;
201 }
202 
203 void FastWriter::writeValue(const Value& value) {
204  switch (value.type()) {
205  case nullValue:
206  if (!dropNullPlaceholders_)
207  document_ += "null";
208  break;
209  case intValue:
210  document_ += valueToString(value.asLargestInt());
211  break;
212  case uintValue:
213  document_ += valueToString(value.asLargestUInt());
214  break;
215  case realValue:
216  document_ += valueToString(value.asDouble());
217  break;
218  case stringValue:
219  document_ += valueToQuotedString(value.asCString());
220  break;
221  case booleanValue:
222  document_ += valueToString(value.asBool());
223  break;
224  case arrayValue: {
225  document_ += "[";
226  int size = value.size();
227  for (int index = 0; index < size; ++index) {
228  if (index > 0)
229  document_ += ",";
230  writeValue(value[index]);
231  }
232  document_ += "]";
233  } break;
234  case objectValue: {
235  Value::Members members(value.getMemberNames());
236  document_ += "{";
237  for (Value::Members::iterator it = members.begin(); it != members.end();
238  ++it) {
239  const std::string& name = *it;
240  if (it != members.begin())
241  document_ += ",";
242  document_ += valueToQuotedString(name.c_str());
243  document_ += yamlCompatiblityEnabled_ ? ": " : ":";
244  writeValue(value[name]);
245  }
246  document_ += "}";
247  } break;
248  }
249 }
250 
251 // Class StyledWriter
252 // //////////////////////////////////////////////////////////////////
253 
255  : rightMargin_(74), indentSize_(3), addChildValues_() {}
256 
257 std::string StyledWriter::write(const Value& root) {
258  document_ = "";
259  addChildValues_ = false;
260  indentString_ = "";
261  writeCommentBeforeValue(root);
262  writeValue(root);
263  writeCommentAfterValueOnSameLine(root);
264  document_ += "\n";
265  return document_;
266 }
267 
268 void StyledWriter::writeValue(const Value& value) {
269  switch (value.type()) {
270  case nullValue:
271  pushValue("null");
272  break;
273  case intValue:
274  pushValue(valueToString(value.asLargestInt()));
275  break;
276  case uintValue:
277  pushValue(valueToString(value.asLargestUInt()));
278  break;
279  case realValue:
280  pushValue(valueToString(value.asDouble()));
281  break;
282  case stringValue:
283  pushValue(valueToQuotedString(value.asCString()));
284  break;
285  case booleanValue:
286  pushValue(valueToString(value.asBool()));
287  break;
288  case arrayValue:
289  writeArrayValue(value);
290  break;
291  case objectValue: {
292  Value::Members members(value.getMemberNames());
293  if (members.empty())
294  pushValue("{}");
295  else {
296  writeWithIndent("{");
297  indent();
298  Value::Members::iterator it = members.begin();
299  for (;;) {
300  const std::string& name = *it;
301  const Value& childValue = value[name];
302  writeCommentBeforeValue(childValue);
303  writeWithIndent(valueToQuotedString(name.c_str()));
304  document_ += " : ";
305  writeValue(childValue);
306  if (++it == members.end()) {
307  writeCommentAfterValueOnSameLine(childValue);
308  break;
309  }
310  document_ += ",";
311  writeCommentAfterValueOnSameLine(childValue);
312  }
313  unindent();
314  writeWithIndent("}");
315  }
316  } break;
317  }
318 }
319 
320 void StyledWriter::writeArrayValue(const Value& value) {
321  unsigned size = value.size();
322  if (size == 0)
323  pushValue("[]");
324  else {
325  bool isArrayMultiLine = isMultineArray(value);
326  if (isArrayMultiLine) {
327  writeWithIndent("[");
328  indent();
329  bool hasChildValue = !childValues_.empty();
330  unsigned index = 0;
331  for (;;) {
332  const Value& childValue = value[index];
333  writeCommentBeforeValue(childValue);
334  if (hasChildValue)
335  writeWithIndent(childValues_[index]);
336  else {
337  writeIndent();
338  writeValue(childValue);
339  }
340  if (++index == size) {
341  writeCommentAfterValueOnSameLine(childValue);
342  break;
343  }
344  document_ += ",";
345  writeCommentAfterValueOnSameLine(childValue);
346  }
347  unindent();
348  writeWithIndent("]");
349  } else // output on a single line
350  {
351  assert(childValues_.size() == size);
352  document_ += "[ ";
353  for (unsigned index = 0; index < size; ++index) {
354  if (index > 0)
355  document_ += ", ";
356  document_ += childValues_[index];
357  }
358  document_ += " ]";
359  }
360  }
361 }
362 
363 bool StyledWriter::isMultineArray(const Value& value) {
364  int size = value.size();
365  bool isMultiLine = size * 3 >= rightMargin_;
366  childValues_.clear();
367  for (int index = 0; index < size && !isMultiLine; ++index) {
368  const Value& childValue = value[index];
369  isMultiLine =
370  isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
371  childValue.size() > 0);
372  }
373  if (!isMultiLine) // check if line length > max line length
374  {
375  childValues_.reserve(size);
376  addChildValues_ = true;
377  int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
378  for (int index = 0; index < size; ++index) {
379  writeValue(value[index]);
380  lineLength += int(childValues_[index].length());
381  }
382  addChildValues_ = false;
383  isMultiLine = isMultiLine || lineLength >= rightMargin_;
384  }
385  return isMultiLine;
386 }
387 
388 void StyledWriter::pushValue(const std::string& value) {
389  if (addChildValues_)
390  childValues_.push_back(value);
391  else
392  document_ += value;
393 }
394 
395 void StyledWriter::writeIndent() {
396  if (!document_.empty()) {
397  char last = document_[document_.length() - 1];
398  if (last == ' ') // already indented
399  return;
400  if (last != '\n') // Comments may add new-line
401  document_ += '\n';
402  }
403  document_ += indentString_;
404 }
405 
406 void StyledWriter::writeWithIndent(const std::string& value) {
407  writeIndent();
408  document_ += value;
409 }
410 
411 void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
412 
413 void StyledWriter::unindent() {
414  assert(int(indentString_.size()) >= indentSize_);
415  indentString_.resize(indentString_.size() - indentSize_);
416 }
417 
418 void StyledWriter::writeCommentBeforeValue(const Value& root) {
419  if (!root.hasComment(commentBefore))
420  return;
421 
422  document_ += "\n";
423  writeIndent();
424  std::string normalizedComment = normalizeEOL(root.getComment(commentBefore));
425  std::string::const_iterator iter = normalizedComment.begin();
426  while (iter != normalizedComment.end()) {
427  document_ += *iter;
428  if (*iter == '\n' && *(iter + 1) == '/')
429  writeIndent();
430  ++iter;
431  }
432 
433  // Comments are stripped of newlines, so add one here
434  document_ += "\n";
435 }
436 
437 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
438  if (root.hasComment(commentAfterOnSameLine))
439  document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
440 
441  if (root.hasComment(commentAfter)) {
442  document_ += "\n";
443  document_ += normalizeEOL(root.getComment(commentAfter));
444  document_ += "\n";
445  }
446 }
447 
448 bool StyledWriter::hasCommentForValue(const Value& value) {
449  return value.hasComment(commentBefore) ||
450  value.hasComment(commentAfterOnSameLine) ||
451  value.hasComment(commentAfter);
452 }
453 
454 std::string StyledWriter::normalizeEOL(const std::string& text) {
455  std::string normalized;
456  normalized.reserve(text.length());
457  const char* begin = text.c_str();
458  const char* end = begin + text.length();
459  const char* current = begin;
460  while (current != end) {
461  char c = *current++;
462  if (c == '\r') // mac or dos EOL
463  {
464  if (*current == '\n') // convert dos EOL
465  ++current;
466  normalized += '\n';
467  } else // handle unix EOL & other char
468  normalized += c;
469  }
470  return normalized;
471 }
472 
473 // Class StyledStreamWriter
474 // //////////////////////////////////////////////////////////////////
475 
477  : document_(NULL), rightMargin_(74), indentation_(indentation),
478  addChildValues_() {}
479 
480 void StyledStreamWriter::write(std::ostream& out, const Value& root) {
481  document_ = &out;
482  addChildValues_ = false;
483  indentString_ = "";
484  writeCommentBeforeValue(root);
485  writeValue(root);
486  writeCommentAfterValueOnSameLine(root);
487  *document_ << "\n";
488  document_ = NULL; // Forget the stream, for safety.
489 }
490 
491 void StyledStreamWriter::writeValue(const Value& value) {
492  switch (value.type()) {
493  case nullValue:
494  pushValue("null");
495  break;
496  case intValue:
497  pushValue(valueToString(value.asLargestInt()));
498  break;
499  case uintValue:
500  pushValue(valueToString(value.asLargestUInt()));
501  break;
502  case realValue:
503  pushValue(valueToString(value.asDouble()));
504  break;
505  case stringValue:
506  pushValue(valueToQuotedString(value.asCString()));
507  break;
508  case booleanValue:
509  pushValue(valueToString(value.asBool()));
510  break;
511  case arrayValue:
512  writeArrayValue(value);
513  break;
514  case objectValue: {
515  Value::Members members(value.getMemberNames());
516  if (members.empty())
517  pushValue("{}");
518  else {
519  writeWithIndent("{");
520  indent();
521  Value::Members::iterator it = members.begin();
522  for (;;) {
523  const std::string& name = *it;
524  const Value& childValue = value[name];
525  writeCommentBeforeValue(childValue);
526  writeWithIndent(valueToQuotedString(name.c_str()));
527  *document_ << " : ";
528  writeValue(childValue);
529  if (++it == members.end()) {
530  writeCommentAfterValueOnSameLine(childValue);
531  break;
532  }
533  *document_ << ",";
534  writeCommentAfterValueOnSameLine(childValue);
535  }
536  unindent();
537  writeWithIndent("}");
538  }
539  } break;
540  }
541 }
542 
543 void StyledStreamWriter::writeArrayValue(const Value& value) {
544  unsigned size = value.size();
545  if (size == 0)
546  pushValue("[]");
547  else {
548  bool isArrayMultiLine = isMultineArray(value);
549  if (isArrayMultiLine) {
550  writeWithIndent("[");
551  indent();
552  bool hasChildValue = !childValues_.empty();
553  unsigned index = 0;
554  for (;;) {
555  const Value& childValue = value[index];
556  writeCommentBeforeValue(childValue);
557  if (hasChildValue)
558  writeWithIndent(childValues_[index]);
559  else {
560  writeIndent();
561  writeValue(childValue);
562  }
563  if (++index == size) {
564  writeCommentAfterValueOnSameLine(childValue);
565  break;
566  }
567  *document_ << ",";
568  writeCommentAfterValueOnSameLine(childValue);
569  }
570  unindent();
571  writeWithIndent("]");
572  } else // output on a single line
573  {
574  assert(childValues_.size() == size);
575  *document_ << "[ ";
576  for (unsigned index = 0; index < size; ++index) {
577  if (index > 0)
578  *document_ << ", ";
579  *document_ << childValues_[index];
580  }
581  *document_ << " ]";
582  }
583  }
584 }
585 
586 bool StyledStreamWriter::isMultineArray(const Value& value) {
587  int size = value.size();
588  bool isMultiLine = size * 3 >= rightMargin_;
589  childValues_.clear();
590  for (int index = 0; index < size && !isMultiLine; ++index) {
591  const Value& childValue = value[index];
592  isMultiLine =
593  isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
594  childValue.size() > 0);
595  }
596  if (!isMultiLine) // check if line length > max line length
597  {
598  childValues_.reserve(size);
599  addChildValues_ = true;
600  int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
601  for (int index = 0; index < size; ++index) {
602  writeValue(value[index]);
603  lineLength += int(childValues_[index].length());
604  }
605  addChildValues_ = false;
606  isMultiLine = isMultiLine || lineLength >= rightMargin_;
607  }
608  return isMultiLine;
609 }
610 
611 void StyledStreamWriter::pushValue(const std::string& value) {
612  if (addChildValues_)
613  childValues_.push_back(value);
614  else
615  *document_ << value;
616 }
617 
618 void StyledStreamWriter::writeIndent() {
619  /*
620  Some comments in this method would have been nice. ;-)
621 
622  if ( !document_.empty() )
623  {
624  char last = document_[document_.length()-1];
625  if ( last == ' ' ) // already indented
626  return;
627  if ( last != '\n' ) // Comments may add new-line
628  *document_ << '\n';
629  }
630  */
631  *document_ << '\n' << indentString_;
632 }
633 
634 void StyledStreamWriter::writeWithIndent(const std::string& value) {
635  writeIndent();
636  *document_ << value;
637 }
638 
639 void StyledStreamWriter::indent() { indentString_ += indentation_; }
640 
641 void StyledStreamWriter::unindent() {
642  assert(indentString_.size() >= indentation_.size());
643  indentString_.resize(indentString_.size() - indentation_.size());
644 }
645 
646 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
647  if (!root.hasComment(commentBefore))
648  return;
649  *document_ << normalizeEOL(root.getComment(commentBefore));
650  *document_ << "\n";
651 }
652 
653 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
654  if (root.hasComment(commentAfterOnSameLine))
655  *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
656 
657  if (root.hasComment(commentAfter)) {
658  *document_ << "\n";
659  *document_ << normalizeEOL(root.getComment(commentAfter));
660  *document_ << "\n";
661  }
662 }
663 
664 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
665  return value.hasComment(commentBefore) ||
666  value.hasComment(commentAfterOnSameLine) ||
667  value.hasComment(commentAfter);
668 }
669 
670 std::string StyledStreamWriter::normalizeEOL(const std::string& text) {
671  std::string normalized;
672  normalized.reserve(text.length());
673  const char* begin = text.c_str();
674  const char* end = begin + text.length();
675  const char* current = begin;
676  while (current != end) {
677  char c = *current++;
678  if (c == '\r') // mac or dos EOL
679  {
680  if (*current == '\n') // convert dos EOL
681  ++current;
682  normalized += '\n';
683  } else // handle unix EOL & other char
684  normalized += c;
685  }
686  return normalized;
687 }
688 
689 std::ostream& operator<<(std::ostream& sout, const Value& root) {
691  writer.write(sout, root);
692  return sout;
693 }
694 
695 } // namespace Json
Int64 LargestInt
Definition: config.h:106
void omitEndingLineFeed()
#define snprintf
Definition: json_writer.cpp:21
static void uintToString(LargestUInt value, char *&current)
Converts an unsigned integer to string.
Definition: json_tool.h:63
std::vector< std::string > Members
Definition: value.h:123
double asDouble() const
Definition: json_value.cpp:733
array value (ordered list)
Definition: value.h:44
LargestUInt asLargestUInt() const
Definition: json_value.cpp:725
unsigned integer value
Definition: value.h:40
std::string valueToQuotedString(const char *value)
object value (collection of name/value pairs).
Definition: value.h:45
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:20
bool asBool() const
Definition: json_value.cpp:777
static void fixNumericLocale(char *begin, char *end)
Change ',' to '.
Definition: json_tool.h:76
'null' value
Definition: value.h:38
UInt64 LargestUInt
Definition: config.h:107
std::string valueToString(Int value)
Definition: json_writer.cpp:67
virtual std::string write(const Value &root)
Serialize a Value in JSON format.
Members getMemberNames() const
Return a list of the member names.
const char * asCString() const
Definition: json_value.cpp:597
double value
Definition: value.h:41
virtual ~Writer()
ArrayIndex size() const
Number of values in array or object.
Definition: json_value.cpp:829
Represents a JSON value.
Definition: value.h:116
ValueType type() const
Definition: json_value.cpp:500
a comment on the line after a value (only make sense for
Definition: value.h:51
LargestInt asLargestInt() const
Definition: json_value.cpp:717
unsigned int UInt
Definition: config.h:92
Writes a Value in JSON format in a human friendly way, to a stream rather than to a string...
Definition: writer.h:155
void dropNullPlaceholders()
Drop the "null" string from the writer's output for nullValues.
bool value
Definition: value.h:43
static bool containsControlCharacter(const char *str)
Definition: json_writer.cpp:36
signed integer value
Definition: value.h:39
int Int
Definition: config.h:91
a comment placed on the line before a value
Definition: value.h:49
UTF-8 string value.
Definition: value.h:42
a comment just after a value on the same line
Definition: value.h:50
std::ostream & operator<<(std::ostream &, const Value &root)
Output using the StyledStreamWriter.