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
Write(const Value * const node,std::string * json)25 void JSONWriter::Write(const Value* const node, std::string* json) {
26 WriteWithOptions(node, 0, json);
27 }
28
29 // static
WriteWithOptions(const Value * const node,int options,std::string * json)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
JSONWriter(bool omit_binary_values,bool omit_double_type_preservation,bool pretty_print,std::string * json)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
BuildJSONString(const Value * const node,int depth)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
IndentLine(int depth)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