json_writer.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
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
14namespace base {
15
16#if defined(OS_WIN)
17static const char kPrettyPrintLineEnding[] = "\r\n";
18#else
19static const char kPrettyPrintLineEnding[] = "\n";
20#endif
21
22/* static */
23const char* JSONWriter::kEmptyArray = "[]";
24
25/* static */
26void 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 */
33void 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
46JSONWriter::JSONWriter(bool pretty_print, std::string* json)
47    : json_string_(json),
48      pretty_print_(pretty_print) {
49  DCHECK(json);
50}
51
52void 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_REAL:
79      {
80        double value;
81        bool result = node->GetAsReal(&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
191void 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
197void 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