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