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 */
Write(const Value * const node,bool pretty_print,std::string * json)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 */
WriteWithOptionalEscape(const Value * const node,bool pretty_print,bool escape,std::string * json)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
JSONWriter(bool pretty_print,std::string * json)46 JSONWriter::JSONWriter(bool pretty_print, std::string* json)
47 : json_string_(json),
48 pretty_print_(pretty_print) {
49 DCHECK(json);
50 }
51
BuildJSONString(const Value * const node,int depth,bool escape)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
AppendQuotedString(const std::string & str)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
IndentLine(int depth)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