JsonCpp project page JsonCpp home page

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

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