• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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