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