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