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/utf_string_conversions.h"
     13 #include "base/values.h"
     14 
     15 namespace base {
     16 
     17 #if defined(OS_WIN)
     18 const char kPrettyPrintLineEnding[] = "\r\n";
     19 #else
     20 const char kPrettyPrintLineEnding[] = "\n";
     21 #endif
     22 
     23 // static
     24 bool JSONWriter::Write(const Value* const node, std::string* json) {
     25   return WriteWithOptions(node, 0, json);
     26 }
     27 
     28 // static
     29 bool JSONWriter::WriteWithOptions(const Value* const node, int options,
     30                                   std::string* json) {
     31   json->clear();
     32   // Is there a better way to estimate the size of the output?
     33   json->reserve(1024);
     34 
     35   JSONWriter writer(options, json);
     36   bool result = writer.BuildJSONString(node, 0U);
     37 
     38   if (options & OPTIONS_PRETTY_PRINT)
     39     json->append(kPrettyPrintLineEnding);
     40 
     41   return result;
     42 }
     43 
     44 JSONWriter::JSONWriter(int options, std::string* json)
     45     : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
     46       omit_double_type_preservation_(
     47           (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0),
     48       pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
     49       json_string_(json) {
     50   DCHECK(json);
     51 }
     52 
     53 bool JSONWriter::BuildJSONString(const Value* const node, size_t depth) {
     54   switch (node->GetType()) {
     55     case Value::TYPE_NULL: {
     56       json_string_->append("null");
     57       return true;
     58     }
     59 
     60     case Value::TYPE_BOOLEAN: {
     61       bool value;
     62       bool result = node->GetAsBoolean(&value);
     63       DCHECK(result);
     64       json_string_->append(value ? "true" : "false");
     65       return result;
     66     }
     67 
     68     case Value::TYPE_INTEGER: {
     69       int value;
     70       bool result = node->GetAsInteger(&value);
     71       DCHECK(result);
     72       json_string_->append(IntToString(value));
     73       return result;
     74     }
     75 
     76     case Value::TYPE_DOUBLE: {
     77       double value;
     78       bool result = node->GetAsDouble(&value);
     79       DCHECK(result);
     80       if (omit_double_type_preservation_ &&
     81           value <= kint64max &&
     82           value >= kint64min &&
     83           std::floor(value) == value) {
     84         json_string_->append(Int64ToString(static_cast<int64>(value)));
     85         return result;
     86       }
     87       std::string real = DoubleToString(value);
     88       // Ensure that the number has a .0 if there's no decimal or 'e'.  This
     89       // makes sure that when we read the JSON back, it's interpreted as a
     90       // real rather than an int.
     91       if (real.find('.') == std::string::npos &&
     92           real.find('e') == std::string::npos &&
     93           real.find('E') == std::string::npos) {
     94         real.append(".0");
     95       }
     96       // The JSON spec requires that non-integer values in the range (-1,1)
     97       // have a zero before the decimal point - ".52" is not valid, "0.52" is.
     98       if (real[0] == '.') {
     99         real.insert(static_cast<size_t>(0), static_cast<size_t>(1), '0');
    100       } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
    101         // "-.1" bad "-0.1" good
    102         real.insert(static_cast<size_t>(1), static_cast<size_t>(1), '0');
    103       }
    104       json_string_->append(real);
    105       return result;
    106     }
    107 
    108     case Value::TYPE_STRING: {
    109       std::string value;
    110       bool result = node->GetAsString(&value);
    111       DCHECK(result);
    112       EscapeJSONString(value, true, json_string_);
    113       return result;
    114     }
    115 
    116     case Value::TYPE_LIST: {
    117       json_string_->push_back('[');
    118       if (pretty_print_)
    119         json_string_->push_back(' ');
    120 
    121       const ListValue* list = NULL;
    122       bool first_value_has_been_output = false;
    123       bool result = node->GetAsList(&list);
    124       DCHECK(result);
    125       for (ListValue::const_iterator it = list->begin(); it != list->end();
    126            ++it) {
    127         const Value* value = *it;
    128         if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY)
    129           continue;
    130 
    131         if (first_value_has_been_output) {
    132           json_string_->push_back(',');
    133           if (pretty_print_)
    134             json_string_->push_back(' ');
    135         }
    136 
    137         if (!BuildJSONString(value, depth))
    138           result = false;
    139 
    140         first_value_has_been_output = true;
    141       }
    142 
    143       if (pretty_print_)
    144         json_string_->push_back(' ');
    145       json_string_->push_back(']');
    146       return result;
    147     }
    148 
    149     case Value::TYPE_DICTIONARY: {
    150       json_string_->push_back('{');
    151       if (pretty_print_)
    152         json_string_->append(kPrettyPrintLineEnding);
    153 
    154       const DictionaryValue* dict = NULL;
    155       bool first_value_has_been_output = false;
    156       bool result = node->GetAsDictionary(&dict);
    157       DCHECK(result);
    158       for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
    159            itr.Advance()) {
    160         if (omit_binary_values_ &&
    161             itr.value().GetType() == Value::TYPE_BINARY) {
    162           continue;
    163         }
    164 
    165         if (first_value_has_been_output) {
    166           json_string_->push_back(',');
    167           if (pretty_print_)
    168             json_string_->append(kPrettyPrintLineEnding);
    169         }
    170 
    171         if (pretty_print_)
    172           IndentLine(depth + 1U);
    173 
    174         EscapeJSONString(itr.key(), true, json_string_);
    175         json_string_->push_back(':');
    176         if (pretty_print_)
    177           json_string_->push_back(' ');
    178 
    179         if (!BuildJSONString(&itr.value(), depth + 1U))
    180           result = false;
    181 
    182         first_value_has_been_output = true;
    183       }
    184 
    185       if (pretty_print_) {
    186         json_string_->append(kPrettyPrintLineEnding);
    187         IndentLine(depth);
    188       }
    189 
    190       json_string_->push_back('}');
    191       return result;
    192     }
    193 
    194     case Value::TYPE_BINARY:
    195       // Successful only if we're allowed to omit it.
    196       DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
    197       return omit_binary_values_;
    198   }
    199   NOTREACHED();
    200   return false;
    201 }
    202 
    203 void JSONWriter::IndentLine(size_t depth) {
    204   json_string_->append(depth * 3U, ' ');
    205 }
    206 
    207 }  // namespace base
    208