Home | History | Annotate | Download | only in json
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/json/json_writer.h"
      6 
      7 #include <cmath>
      8 
      9 #include "base/json/string_escape.h"
     10 #include "base/logging.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/values.h"
     15 
     16 namespace base {
     17 
     18 #if defined(OS_WIN)
     19 static const char kPrettyPrintLineEnding[] = "\r\n";
     20 #else
     21 static const char kPrettyPrintLineEnding[] = "\n";
     22 #endif
     23 
     24 /* static */
     25 const char* JSONWriter::kEmptyArray = "[]";
     26 
     27 /* static */
     28 void JSONWriter::Write(const Value* const node, std::string* json) {
     29   WriteWithOptions(node, 0, json);
     30 }
     31 
     32 /* static */
     33 void JSONWriter::WriteWithOptions(const Value* const node, int options,
     34                                   std::string* json) {
     35   json->clear();
     36   // Is there a better way to estimate the size of the output?
     37   json->reserve(1024);
     38 
     39   bool escape = !(options & OPTIONS_DO_NOT_ESCAPE);
     40   bool omit_binary_values = !!(options & OPTIONS_OMIT_BINARY_VALUES);
     41   bool omit_double_type_preservation =
     42       !!(options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION);
     43   bool pretty_print = !!(options & OPTIONS_PRETTY_PRINT);
     44 
     45   JSONWriter writer(escape, omit_binary_values, omit_double_type_preservation,
     46                     pretty_print, json);
     47   writer.BuildJSONString(node, 0);
     48 
     49   if (pretty_print)
     50     json->append(kPrettyPrintLineEnding);
     51 }
     52 
     53 JSONWriter::JSONWriter(bool escape, bool omit_binary_values,
     54                        bool omit_double_type_preservation, bool pretty_print,
     55                        std::string* json)
     56     : escape_(escape),
     57       omit_binary_values_(omit_binary_values),
     58       omit_double_type_preservation_(omit_double_type_preservation),
     59       pretty_print_(pretty_print),
     60       json_string_(json) {
     61   DCHECK(json);
     62 }
     63 
     64 void JSONWriter::BuildJSONString(const Value* const node, int depth) {
     65   switch (node->GetType()) {
     66     case Value::TYPE_NULL:
     67       json_string_->append("null");
     68       break;
     69 
     70     case Value::TYPE_BOOLEAN:
     71       {
     72         bool value;
     73         bool result = node->GetAsBoolean(&value);
     74         DCHECK(result);
     75         json_string_->append(value ? "true" : "false");
     76         break;
     77       }
     78 
     79     case Value::TYPE_INTEGER:
     80       {
     81         int value;
     82         bool result = node->GetAsInteger(&value);
     83         DCHECK(result);
     84         base::StringAppendF(json_string_, "%d", value);
     85         break;
     86       }
     87 
     88     case Value::TYPE_DOUBLE:
     89       {
     90         double value;
     91         bool result = node->GetAsDouble(&value);
     92         DCHECK(result);
     93         if (omit_double_type_preservation_ &&
     94             value <= kint64max &&
     95             value >= kint64min &&
     96             std::floor(value) == value) {
     97           json_string_->append(Int64ToString(static_cast<int64>(value)));
     98           break;
     99         }
    100         std::string real = DoubleToString(value);
    101         // Ensure that the number has a .0 if there's no decimal or 'e'.  This
    102         // makes sure that when we read the JSON back, it's interpreted as a
    103         // real rather than an int.
    104         if (real.find('.') == std::string::npos &&
    105             real.find('e') == std::string::npos &&
    106             real.find('E') == std::string::npos) {
    107           real.append(".0");
    108         }
    109         // The JSON spec requires that non-integer values in the range (-1,1)
    110         // have a zero before the decimal point - ".52" is not valid, "0.52" is.
    111         if (real[0] == '.') {
    112           real.insert(0, "0");
    113         } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
    114           // "-.1" bad "-0.1" good
    115           real.insert(1, "0");
    116         }
    117         json_string_->append(real);
    118         break;
    119       }
    120 
    121     case Value::TYPE_STRING:
    122       {
    123         std::string value;
    124         bool result = node->GetAsString(&value);
    125         DCHECK(result);
    126         if (escape_) {
    127           JsonDoubleQuote(UTF8ToUTF16(value), true, json_string_);
    128         } else {
    129           JsonDoubleQuote(value, true, json_string_);
    130         }
    131         break;
    132       }
    133 
    134     case Value::TYPE_LIST:
    135       {
    136         json_string_->append("[");
    137         if (pretty_print_)
    138           json_string_->append(" ");
    139 
    140         const ListValue* list = static_cast<const ListValue*>(node);
    141         for (size_t i = 0; i < list->GetSize(); ++i) {
    142           const Value* value = NULL;
    143           bool result = list->Get(i, &value);
    144           DCHECK(result);
    145 
    146           if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY) {
    147             continue;
    148           }
    149 
    150           if (i != 0) {
    151             json_string_->append(",");
    152             if (pretty_print_)
    153               json_string_->append(" ");
    154           }
    155 
    156           BuildJSONString(value, depth);
    157         }
    158 
    159         if (pretty_print_)
    160           json_string_->append(" ");
    161         json_string_->append("]");
    162         break;
    163       }
    164 
    165     case Value::TYPE_DICTIONARY:
    166       {
    167         json_string_->append("{");
    168         if (pretty_print_)
    169           json_string_->append(kPrettyPrintLineEnding);
    170 
    171         const DictionaryValue* dict =
    172           static_cast<const DictionaryValue*>(node);
    173         bool first_entry = true;
    174         for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
    175              itr.Advance(), first_entry = false) {
    176           if (omit_binary_values_ &&
    177               itr.value().GetType() == Value::TYPE_BINARY) {
    178             continue;
    179           }
    180 
    181           if (!first_entry) {
    182             json_string_->append(",");
    183             if (pretty_print_)
    184               json_string_->append(kPrettyPrintLineEnding);
    185           }
    186 
    187           if (pretty_print_)
    188             IndentLine(depth + 1);
    189           AppendQuotedString(itr.key());
    190           if (pretty_print_) {
    191             json_string_->append(": ");
    192           } else {
    193             json_string_->append(":");
    194           }
    195           BuildJSONString(&itr.value(), depth + 1);
    196         }
    197 
    198         if (pretty_print_) {
    199           json_string_->append(kPrettyPrintLineEnding);
    200           IndentLine(depth);
    201           json_string_->append("}");
    202         } else {
    203           json_string_->append("}");
    204         }
    205         break;
    206       }
    207 
    208     case Value::TYPE_BINARY:
    209       {
    210         if (!omit_binary_values_) {
    211           NOTREACHED() << "Cannot serialize binary value.";
    212         }
    213         break;
    214       }
    215 
    216     default:
    217       NOTREACHED() << "unknown json type";
    218   }
    219 }
    220 
    221 void JSONWriter::AppendQuotedString(const std::string& str) {
    222   // TODO(viettrungluu): |str| is UTF-8, not ASCII, so to properly escape it we
    223   // have to convert it to UTF-16. This round-trip is suboptimal.
    224   JsonDoubleQuote(UTF8ToUTF16(str), true, json_string_);
    225 }
    226 
    227 void JSONWriter::IndentLine(int depth) {
    228   // It may be faster to keep an indent string so we don't have to keep
    229   // reallocating.
    230   json_string_->append(std::string(depth * 3, ' '));
    231 }
    232 
    233 }  // namespace base
    234