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