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