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