JsonCpp project page JsonCpp home page

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

SourceForge Logo hosts this site. Send comments to:
Json-cpp Developers