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