Home | History | Annotate | Download | only in lib_json
      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 
     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 
     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 
     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 
     60 std::string valueToString( Int value )
     61 {
     62    return valueToString( LargestInt(value) );
     63 }
     64 
     65 
     66 std::string valueToString( UInt value )
     67 {
     68    return valueToString( LargestUInt(value) );
     69 }
     70 
     71 #endif // # if defined(JSON_HAS_INT64)
     72 
     73 
     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 
    114 std::string valueToString( bool value )
    115 {
    116    return value ? "true" : "false";
    117 }
    118 
    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 // //////////////////////////////////////////////////////////////////
    186 Writer::~Writer()
    187 {
    188 }
    189 
    190 
    191 // Class FastWriter
    192 // //////////////////////////////////////////////////////////////////
    193 
    194 FastWriter::FastWriter()
    195    : yamlCompatiblityEnabled_( false )
    196 {
    197 }
    198 
    199 
    200 void
    201 FastWriter::enableYAMLCompatibility()
    202 {
    203    yamlCompatiblityEnabled_ = true;
    204 }
    205 
    206 
    207 std::string
    208 FastWriter::write( const Value &root )
    209 {
    210    document_ = "";
    211    writeValue( root );
    212    document_ += "\n";
    213    return document_;
    214 }
    215 
    216 
    217 void
    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 
    279 StyledWriter::StyledWriter()
    280    : rightMargin_( 74 )
    281    , indentSize_( 3 )
    282    , addChildValues_()
    283 {
    284 }
    285 
    286 
    287 std::string
    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
    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
    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
    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
    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
    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
    472 StyledWriter::writeWithIndent( const std::string &value )
    473 {
    474    writeIndent();
    475    document_ += value;
    476 }
    477 
    478 
    479 void
    480 StyledWriter::indent()
    481 {
    482    indentString_ += std::string( indentSize_, ' ' );
    483 }
    484 
    485 
    486 void
    487 StyledWriter::unindent()
    488 {
    489    assert( int(indentString_.size()) >= indentSize_ );
    490    indentString_.resize( indentString_.size() - indentSize_ );
    491 }
    492 
    493 
    494 void
    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
    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
    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
    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 
    555 StyledStreamWriter::StyledStreamWriter( std::string indentation )
    556    : document_(NULL)
    557    , rightMargin_( 74 )
    558    , indentation_( indentation )
    559    , addChildValues_()
    560 {
    561 }
    562 
    563 
    564 void
    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
    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
    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
    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
    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
    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
    753 StyledStreamWriter::writeWithIndent( const std::string &value )
    754 {
    755    writeIndent();
    756    *document_ << value;
    757 }
    758 
    759 
    760 void
    761 StyledStreamWriter::indent()
    762 {
    763    indentString_ += indentation_;
    764 }
    765 
    766 
    767 void
    768 StyledStreamWriter::unindent()
    769 {
    770    assert( indentString_.size() >= indentation_.size() );
    771    indentString_.resize( indentString_.size() - indentation_.size() );
    772 }
    773 
    774 
    775 void
    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
    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
    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
    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 
    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