JsonCpp project page JsonCpp home page

json_writer.cpp

Go to the documentation of this file.
00001 // Copyright 2007-2010 Baptiste Lepilleur
00002 // Distributed under MIT license, or public domain if desired and
00003 // recognized in your jurisdiction.
00004 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
00005 
00006 #if !defined(JSON_IS_AMALGAMATION)
00007 # include <json/writer.h>
00008 # include "json_tool.h"
00009 #endif // if !defined(JSON_IS_AMALGAMATION)
00010 #include <utility>
00011 #include <assert.h>
00012 #include <stdio.h>
00013 #include <string.h>
00014 #include <iostream>
00015 #include <sstream>
00016 #include <iomanip>
00017 
00018 #if _MSC_VER >= 1400 // VC++ 8.0
00019 #pragma warning( disable : 4996 )   // disable warning about strdup being deprecated.
00020 #endif
00021 
00022 namespace Json {
00023 
00024 static bool containsControlCharacter( const char* str )
00025 {
00026    while ( *str ) 
00027    {
00028       if ( isControlCharacter( *(str++) ) )
00029          return true;
00030    }
00031    return false;
00032 }
00033 
00034 
00035 std::string valueToString( LargestInt value )
00036 {
00037    UIntToStringBuffer buffer;
00038    char *current = buffer + sizeof(buffer);
00039    bool isNegative = value < 0;
00040    if ( isNegative )
00041       value = -value;
00042    uintToString( LargestUInt(value), current );
00043    if ( isNegative )
00044       *--current = '-';
00045    assert( current >= buffer );
00046    return current;
00047 }
00048 
00049 
00050 std::string valueToString( LargestUInt value )
00051 {
00052    UIntToStringBuffer buffer;
00053    char *current = buffer + sizeof(buffer);
00054    uintToString( value, current );
00055    assert( current >= buffer );
00056    return current;
00057 }
00058 
00059 #if defined(JSON_HAS_INT64)
00060 
00061 std::string valueToString( Int value )
00062 {
00063    return valueToString( LargestInt(value) );
00064 }
00065 
00066 
00067 std::string valueToString( UInt value )
00068 {
00069    return valueToString( LargestUInt(value) );
00070 }
00071 
00072 #endif // # if defined(JSON_HAS_INT64)
00073 
00074 
00075 std::string valueToString( double value )
00076 {
00077    char buffer[32];
00078 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. 
00079    sprintf_s(buffer, sizeof(buffer), "%#.16g", value); 
00080 #else 
00081    sprintf(buffer, "%#.16g", value); 
00082 #endif
00083    char* ch = buffer + strlen(buffer) - 1;
00084    if (*ch != '0') return buffer; // nothing to truncate, so save time
00085    while(ch > buffer && *ch == '0'){
00086      --ch;
00087    }
00088    char* last_nonzero = ch;
00089    while(ch >= buffer){
00090      switch(*ch){
00091      case '0':
00092      case '1':
00093      case '2':
00094      case '3':
00095      case '4':
00096      case '5':
00097      case '6':
00098      case '7':
00099      case '8':
00100      case '9':
00101        --ch;
00102        continue;
00103      case '.':
00104        // Truncate zeroes to save bytes in output, but keep one.
00105        *(last_nonzero+2) = '\0';
00106        return buffer;
00107      default:
00108        return buffer;
00109      }
00110    }
00111    return buffer;
00112 }
00113 
00114 
00115 std::string valueToString( bool value )
00116 {
00117    return value ? "true" : "false";
00118 }
00119 
00120 std::string valueToQuotedString( const char *value )
00121 {
00122    // Not sure how to handle unicode...
00123    if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
00124       return std::string("\"") + value + "\"";
00125    // We have to walk value and escape any special characters.
00126    // Appending to std::string is not efficient, but this should be rare.
00127    // (Note: forward slashes are *not* rare, but I am not escaping them.)
00128    std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
00129    std::string result;
00130    result.reserve(maxsize); // to avoid lots of mallocs
00131    result += "\"";
00132    for (const char* c=value; *c != 0; ++c)
00133    {
00134       switch(*c)
00135       {
00136          case '\"':
00137             result += "\\\"";
00138             break;
00139          case '\\':
00140             result += "\\\\";
00141             break;
00142          case '\b':
00143             result += "\\b";
00144             break;
00145          case '\f':
00146             result += "\\f";
00147             break;
00148          case '\n':
00149             result += "\\n";
00150             break;
00151          case '\r':
00152             result += "\\r";
00153             break;
00154          case '\t':
00155             result += "\\t";
00156             break;
00157          //case '/':
00158             // Even though \/ is considered a legal escape in JSON, a bare
00159             // slash is also legal, so I see no reason to escape it.
00160             // (I hope I am not misunderstanding something.
00161             // blep notes: actually escaping \/ may be useful in javascript to avoid </ 
00162             // sequence.
00163             // Should add a flag to allow this compatibility mode and prevent this 
00164             // sequence from occurring.
00165          default:
00166             if ( isControlCharacter( *c ) )
00167             {
00168                std::ostringstream oss;
00169                oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
00170                result += oss.str();
00171             }
00172             else
00173             {
00174                result += *c;
00175             }
00176             break;
00177       }
00178    }
00179    result += "\"";
00180    return result;
00181 }
00182 
00183 // Class Writer
00184 // //////////////////////////////////////////////////////////////////
00185 Writer::~Writer()
00186 {
00187 }
00188 
00189 
00190 // Class FastWriter
00191 // //////////////////////////////////////////////////////////////////
00192 
00193 FastWriter::FastWriter()
00194    : yamlCompatiblityEnabled_( false )
00195 {
00196 }
00197 
00198 
00199 void 
00200 FastWriter::enableYAMLCompatibility()
00201 {
00202    yamlCompatiblityEnabled_ = true;
00203 }
00204 
00205 
00206 std::string 
00207 FastWriter::write( const Value &root )
00208 {
00209    document_ = "";
00210    writeValue( root );
00211    document_ += "\n";
00212    return document_;
00213 }
00214 
00215 
00216 void 
00217 FastWriter::writeValue( const Value &value )
00218 {
00219    switch ( value.type() )
00220    {
00221    case nullValue:
00222       document_ += "null";
00223       break;
00224    case intValue:
00225       document_ += valueToString( value.asLargestInt() );
00226       break;
00227    case uintValue:
00228       document_ += valueToString( value.asLargestUInt() );
00229       break;
00230    case realValue:
00231       document_ += valueToString( value.asDouble() );
00232       break;
00233    case stringValue:
00234       document_ += valueToQuotedString( value.asCString() );
00235       break;
00236    case booleanValue:
00237       document_ += valueToString( value.asBool() );
00238       break;
00239    case arrayValue:
00240       {
00241          document_ += "[";
00242          int size = value.size();
00243          for ( int index =0; index < size; ++index )
00244          {
00245             if ( index > 0 )
00246                document_ += ",";
00247             writeValue( value[index] );
00248          }
00249          document_ += "]";
00250       }
00251       break;
00252    case objectValue:
00253       {
00254          Value::Members members( value.getMemberNames() );
00255          document_ += "{";
00256          for ( Value::Members::iterator it = members.begin(); 
00257                it != members.end(); 
00258                ++it )
00259          {
00260             const std::string &name = *it;
00261             if ( it != members.begin() )
00262                document_ += ",";
00263             document_ += valueToQuotedString( name.c_str() );
00264             document_ += yamlCompatiblityEnabled_ ? ": " 
00265                                                   : ":";
00266             writeValue( value[name] );
00267          }
00268          document_ += "}";
00269       }
00270       break;
00271    }
00272 }
00273 
00274 
00275 // Class StyledWriter
00276 // //////////////////////////////////////////////////////////////////
00277 
00278 StyledWriter::StyledWriter()
00279    : rightMargin_( 74 )
00280    , indentSize_( 3 )
00281 {
00282 }
00283 
00284 
00285 std::string 
00286 StyledWriter::write( const Value &root )
00287 {
00288    document_ = "";
00289    addChildValues_ = false;
00290    indentString_ = "";
00291    writeCommentBeforeValue( root );
00292    writeValue( root );
00293    writeCommentAfterValueOnSameLine( root );
00294    document_ += "\n";
00295    return document_;
00296 }
00297 
00298 
00299 void 
00300 StyledWriter::writeValue( const Value &value )
00301 {
00302    switch ( value.type() )
00303    {
00304    case nullValue:
00305       pushValue( "null" );
00306       break;
00307    case intValue:
00308       pushValue( valueToString( value.asLargestInt() ) );
00309       break;
00310    case uintValue:
00311       pushValue( valueToString( value.asLargestUInt() ) );
00312       break;
00313    case realValue:
00314       pushValue( valueToString( value.asDouble() ) );
00315       break;
00316    case stringValue:
00317       pushValue( valueToQuotedString( value.asCString() ) );
00318       break;
00319    case booleanValue:
00320       pushValue( valueToString( value.asBool() ) );
00321       break;
00322    case arrayValue:
00323       writeArrayValue( value);
00324       break;
00325    case objectValue:
00326       {
00327          Value::Members members( value.getMemberNames() );
00328          if ( members.empty() )
00329             pushValue( "{}" );
00330          else
00331          {
00332             writeWithIndent( "{" );
00333             indent();
00334             Value::Members::iterator it = members.begin();
00335             for (;;)
00336             {
00337                const std::string &name = *it;
00338                const Value &childValue = value[name];
00339                writeCommentBeforeValue( childValue );
00340                writeWithIndent( valueToQuotedString( name.c_str() ) );
00341                document_ += " : ";
00342                writeValue( childValue );
00343                if ( ++it == members.end() )
00344                {
00345                   writeCommentAfterValueOnSameLine( childValue );
00346                   break;
00347                }
00348                document_ += ",";
00349                writeCommentAfterValueOnSameLine( childValue );
00350             }
00351             unindent();
00352             writeWithIndent( "}" );
00353          }
00354       }
00355       break;
00356    }
00357 }
00358 
00359 
00360 void 
00361 StyledWriter::writeArrayValue( const Value &value )
00362 {
00363    unsigned size = value.size();
00364    if ( size == 0 )
00365       pushValue( "[]" );
00366    else
00367    {
00368       bool isArrayMultiLine = isMultineArray( value );
00369       if ( isArrayMultiLine )
00370       {
00371          writeWithIndent( "[" );
00372          indent();
00373          bool hasChildValue = !childValues_.empty();
00374          unsigned index =0;
00375          for (;;)
00376          {
00377             const Value &childValue = value[index];
00378             writeCommentBeforeValue( childValue );
00379             if ( hasChildValue )
00380                writeWithIndent( childValues_[index] );
00381             else
00382             {
00383                writeIndent();
00384                writeValue( childValue );
00385             }
00386             if ( ++index == size )
00387             {
00388                writeCommentAfterValueOnSameLine( childValue );
00389                break;
00390             }
00391             document_ += ",";
00392             writeCommentAfterValueOnSameLine( childValue );
00393          }
00394          unindent();
00395          writeWithIndent( "]" );
00396       }
00397       else // output on a single line
00398       {
00399          assert( childValues_.size() == size );
00400          document_ += "[ ";
00401          for ( unsigned index =0; index < size; ++index )
00402          {
00403             if ( index > 0 )
00404                document_ += ", ";
00405             document_ += childValues_[index];
00406          }
00407          document_ += " ]";
00408       }
00409    }
00410 }
00411 
00412 
00413 bool 
00414 StyledWriter::isMultineArray( const Value &value )
00415 {
00416    int size = value.size();
00417    bool isMultiLine = size*3 >= rightMargin_ ;
00418    childValues_.clear();
00419    for ( int index =0; index < size  &&  !isMultiLine; ++index )
00420    {
00421       const Value &childValue = value[index];
00422       isMultiLine = isMultiLine  ||
00423                      ( (childValue.isArray()  ||  childValue.isObject())  &&  
00424                         childValue.size() > 0 );
00425    }
00426    if ( !isMultiLine ) // check if line length > max line length
00427    {
00428       childValues_.reserve( size );
00429       addChildValues_ = true;
00430       int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
00431       for ( int index =0; index < size  &&  !isMultiLine; ++index )
00432       {
00433          writeValue( value[index] );
00434          lineLength += int( childValues_[index].length() );
00435          isMultiLine = isMultiLine  &&  hasCommentForValue( value[index] );
00436       }
00437       addChildValues_ = false;
00438       isMultiLine = isMultiLine  ||  lineLength >= rightMargin_;
00439    }
00440    return isMultiLine;
00441 }
00442 
00443 
00444 void 
00445 StyledWriter::pushValue( const std::string &value )
00446 {
00447    if ( addChildValues_ )
00448       childValues_.push_back( value );
00449    else
00450       document_ += value;
00451 }
00452 
00453 
00454 void 
00455 StyledWriter::writeIndent()
00456 {
00457    if ( !document_.empty() )
00458    {
00459       char last = document_[document_.length()-1];
00460       if ( last == ' ' )     // already indented
00461          return;
00462       if ( last != '\n' )    // Comments may add new-line
00463          document_ += '\n';
00464    }
00465    document_ += indentString_;
00466 }
00467 
00468 
00469 void 
00470 StyledWriter::writeWithIndent( const std::string &value )
00471 {
00472    writeIndent();
00473    document_ += value;
00474 }
00475 
00476 
00477 void 
00478 StyledWriter::indent()
00479 {
00480    indentString_ += std::string( indentSize_, ' ' );
00481 }
00482 
00483 
00484 void 
00485 StyledWriter::unindent()
00486 {
00487    assert( int(indentString_.size()) >= indentSize_ );
00488    indentString_.resize( indentString_.size() - indentSize_ );
00489 }
00490 
00491 
00492 void 
00493 StyledWriter::writeCommentBeforeValue( const Value &root )
00494 {
00495    if ( !root.hasComment( commentBefore ) )
00496       return;
00497    document_ += normalizeEOL( root.getComment( commentBefore ) );
00498    document_ += "\n";
00499 }
00500 
00501 
00502 void 
00503 StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
00504 {
00505    if ( root.hasComment( commentAfterOnSameLine ) )
00506       document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
00507 
00508    if ( root.hasComment( commentAfter ) )
00509    {
00510       document_ += "\n";
00511       document_ += normalizeEOL( root.getComment( commentAfter ) );
00512       document_ += "\n";
00513    }
00514 }
00515 
00516 
00517 bool 
00518 StyledWriter::hasCommentForValue( const Value &value )
00519 {
00520    return value.hasComment( commentBefore )
00521           ||  value.hasComment( commentAfterOnSameLine )
00522           ||  value.hasComment( commentAfter );
00523 }
00524 
00525 
00526 std::string 
00527 StyledWriter::normalizeEOL( const std::string &text )
00528 {
00529    std::string normalized;
00530    normalized.reserve( text.length() );
00531    const char *begin = text.c_str();
00532    const char *end = begin + text.length();
00533    const char *current = begin;
00534    while ( current != end )
00535    {
00536       char c = *current++;
00537       if ( c == '\r' ) // mac or dos EOL
00538       {
00539          if ( *current == '\n' ) // convert dos EOL
00540             ++current;
00541          normalized += '\n';
00542       }
00543       else // handle unix EOL & other char
00544          normalized += c;
00545    }
00546    return normalized;
00547 }
00548 
00549 
00550 // Class StyledStreamWriter
00551 // //////////////////////////////////////////////////////////////////
00552 
00553 StyledStreamWriter::StyledStreamWriter( std::string indentation )
00554    : document_(NULL)
00555    , rightMargin_( 74 )
00556    , indentation_( indentation )
00557 {
00558 }
00559 
00560 
00561 void
00562 StyledStreamWriter::write( std::ostream &out, const Value &root )
00563 {
00564    document_ = &out;
00565    addChildValues_ = false;
00566    indentString_ = "";
00567    writeCommentBeforeValue( root );
00568    writeValue( root );
00569    writeCommentAfterValueOnSameLine( root );
00570    *document_ << "\n";
00571    document_ = NULL; // Forget the stream, for safety.
00572 }
00573 
00574 
00575 void 
00576 StyledStreamWriter::writeValue( const Value &value )
00577 {
00578    switch ( value.type() )
00579    {
00580    case nullValue:
00581       pushValue( "null" );
00582       break;
00583    case intValue:
00584       pushValue( valueToString( value.asLargestInt() ) );
00585       break;
00586    case uintValue:
00587       pushValue( valueToString( value.asLargestUInt() ) );
00588       break;
00589    case realValue:
00590       pushValue( valueToString( value.asDouble() ) );
00591       break;
00592    case stringValue:
00593       pushValue( valueToQuotedString( value.asCString() ) );
00594       break;
00595    case booleanValue:
00596       pushValue( valueToString( value.asBool() ) );
00597       break;
00598    case arrayValue:
00599       writeArrayValue( value);
00600       break;
00601    case objectValue:
00602       {
00603          Value::Members members( value.getMemberNames() );
00604          if ( members.empty() )
00605             pushValue( "{}" );
00606          else
00607          {
00608             writeWithIndent( "{" );
00609             indent();
00610             Value::Members::iterator it = members.begin();
00611             for (;;)
00612             {
00613                const std::string &name = *it;
00614                const Value &childValue = value[name];
00615                writeCommentBeforeValue( childValue );
00616                writeWithIndent( valueToQuotedString( name.c_str() ) );
00617                *document_ << " : ";
00618                writeValue( childValue );
00619                if ( ++it == members.end() )
00620                {
00621                   writeCommentAfterValueOnSameLine( childValue );
00622                   break;
00623                }
00624                *document_ << ",";
00625                writeCommentAfterValueOnSameLine( childValue );
00626             }
00627             unindent();
00628             writeWithIndent( "}" );
00629          }
00630       }
00631       break;
00632    }
00633 }
00634 
00635 
00636 void 
00637 StyledStreamWriter::writeArrayValue( const Value &value )
00638 {
00639    unsigned size = value.size();
00640    if ( size == 0 )
00641       pushValue( "[]" );
00642    else
00643    {
00644       bool isArrayMultiLine = isMultineArray( value );
00645       if ( isArrayMultiLine )
00646       {
00647          writeWithIndent( "[" );
00648          indent();
00649          bool hasChildValue = !childValues_.empty();
00650          unsigned index =0;
00651          for (;;)
00652          {
00653             const Value &childValue = value[index];
00654             writeCommentBeforeValue( childValue );
00655             if ( hasChildValue )
00656                writeWithIndent( childValues_[index] );
00657             else
00658             {
00659                writeIndent();
00660                writeValue( childValue );
00661             }
00662             if ( ++index == size )
00663             {
00664                writeCommentAfterValueOnSameLine( childValue );
00665                break;
00666             }
00667             *document_ << ",";
00668             writeCommentAfterValueOnSameLine( childValue );
00669          }
00670          unindent();
00671          writeWithIndent( "]" );
00672       }
00673       else // output on a single line
00674       {
00675          assert( childValues_.size() == size );
00676          *document_ << "[ ";
00677          for ( unsigned index =0; index < size; ++index )
00678          {
00679             if ( index > 0 )
00680                *document_ << ", ";
00681             *document_ << childValues_[index];
00682          }
00683          *document_ << " ]";
00684       }
00685    }
00686 }
00687 
00688 
00689 bool 
00690 StyledStreamWriter::isMultineArray( const Value &value )
00691 {
00692    int size = value.size();
00693    bool isMultiLine = size*3 >= rightMargin_ ;
00694    childValues_.clear();
00695    for ( int index =0; index < size  &&  !isMultiLine; ++index )
00696    {
00697       const Value &childValue = value[index];
00698       isMultiLine = isMultiLine  ||
00699                      ( (childValue.isArray()  ||  childValue.isObject())  &&  
00700                         childValue.size() > 0 );
00701    }
00702    if ( !isMultiLine ) // check if line length > max line length
00703    {
00704       childValues_.reserve( size );
00705       addChildValues_ = true;
00706       int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
00707       for ( int index =0; index < size  &&  !isMultiLine; ++index )
00708       {
00709          writeValue( value[index] );
00710          lineLength += int( childValues_[index].length() );
00711          isMultiLine = isMultiLine  &&  hasCommentForValue( value[index] );
00712       }
00713       addChildValues_ = false;
00714       isMultiLine = isMultiLine  ||  lineLength >= rightMargin_;
00715    }
00716    return isMultiLine;
00717 }
00718 
00719 
00720 void 
00721 StyledStreamWriter::pushValue( const std::string &value )
00722 {
00723    if ( addChildValues_ )
00724       childValues_.push_back( value );
00725    else
00726       *document_ << value;
00727 }
00728 
00729 
00730 void 
00731 StyledStreamWriter::writeIndent()
00732 {
00733   /*
00734     Some comments in this method would have been nice. ;-)
00735 
00736    if ( !document_.empty() )
00737    {
00738       char last = document_[document_.length()-1];
00739       if ( last == ' ' )     // already indented
00740          return;
00741       if ( last != '\n' )    // Comments may add new-line
00742          *document_ << '\n';
00743    }
00744   */
00745    *document_ << '\n' << indentString_;
00746 }
00747 
00748 
00749 void 
00750 StyledStreamWriter::writeWithIndent( const std::string &value )
00751 {
00752    writeIndent();
00753    *document_ << value;
00754 }
00755 
00756 
00757 void 
00758 StyledStreamWriter::indent()
00759 {
00760    indentString_ += indentation_;
00761 }
00762 
00763 
00764 void 
00765 StyledStreamWriter::unindent()
00766 {
00767    assert( indentString_.size() >= indentation_.size() );
00768    indentString_.resize( indentString_.size() - indentation_.size() );
00769 }
00770 
00771 
00772 void 
00773 StyledStreamWriter::writeCommentBeforeValue( const Value &root )
00774 {
00775    if ( !root.hasComment( commentBefore ) )
00776       return;
00777    *document_ << normalizeEOL( root.getComment( commentBefore ) );
00778    *document_ << "\n";
00779 }
00780 
00781 
00782 void 
00783 StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
00784 {
00785    if ( root.hasComment( commentAfterOnSameLine ) )
00786       *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
00787 
00788    if ( root.hasComment( commentAfter ) )
00789    {
00790       *document_ << "\n";
00791       *document_ << normalizeEOL( root.getComment( commentAfter ) );
00792       *document_ << "\n";
00793    }
00794 }
00795 
00796 
00797 bool 
00798 StyledStreamWriter::hasCommentForValue( const Value &value )
00799 {
00800    return value.hasComment( commentBefore )
00801           ||  value.hasComment( commentAfterOnSameLine )
00802           ||  value.hasComment( commentAfter );
00803 }
00804 
00805 
00806 std::string 
00807 StyledStreamWriter::normalizeEOL( const std::string &text )
00808 {
00809    std::string normalized;
00810    normalized.reserve( text.length() );
00811    const char *begin = text.c_str();
00812    const char *end = begin + text.length();
00813    const char *current = begin;
00814    while ( current != end )
00815    {
00816       char c = *current++;
00817       if ( c == '\r' ) // mac or dos EOL
00818       {
00819          if ( *current == '\n' ) // convert dos EOL
00820             ++current;
00821          normalized += '\n';
00822       }
00823       else // handle unix EOL & other char
00824          normalized += c;
00825    }
00826    return normalized;
00827 }
00828 
00829 
00830 std::ostream& operator<<( std::ostream &sout, const Value &root )
00831 {
00832    Json::StyledStreamWriter writer;
00833    writer.write(sout, root);
00834    return sout;
00835 }
00836 
00837 
00838 } // namespace Json

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