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/utf_string_conversions.h"
13 #include "base/values.h"
14
15 namespace base {
16
17 #if defined(OS_WIN)
18 const char kPrettyPrintLineEnding[] = "\r\n";
19 #else
20 const char kPrettyPrintLineEnding[] = "\n";
21 #endif
22
23 // static
Write(const Value * const node,std::string * json)24 bool JSONWriter::Write(const Value* const node, std::string* json) {
25 return WriteWithOptions(node, 0, json);
26 }
27
28 // static
WriteWithOptions(const Value * const node,int options,std::string * json)29 bool JSONWriter::WriteWithOptions(const Value* const node, int options,
30 std::string* json) {
31 json->clear();
32 // Is there a better way to estimate the size of the output?
33 json->reserve(1024);
34
35 JSONWriter writer(options, json);
36 bool result = writer.BuildJSONString(node, 0U);
37
38 if (options & OPTIONS_PRETTY_PRINT)
39 json->append(kPrettyPrintLineEnding);
40
41 return result;
42 }
43
JSONWriter(int options,std::string * json)44 JSONWriter::JSONWriter(int options, std::string* json)
45 : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
46 omit_double_type_preservation_(
47 (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0),
48 pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
49 json_string_(json) {
50 DCHECK(json);
51 }
52
BuildJSONString(const Value * const node,size_t depth)53 bool JSONWriter::BuildJSONString(const Value* const node, size_t depth) {
54 switch (node->GetType()) {
55 case Value::TYPE_NULL: {
56 json_string_->append("null");
57 return true;
58 }
59
60 case Value::TYPE_BOOLEAN: {
61 bool value;
62 bool result = node->GetAsBoolean(&value);
63 DCHECK(result);
64 json_string_->append(value ? "true" : "false");
65 return result;
66 }
67
68 case Value::TYPE_INTEGER: {
69 int value;
70 bool result = node->GetAsInteger(&value);
71 DCHECK(result);
72 json_string_->append(IntToString(value));
73 return result;
74 }
75
76 case Value::TYPE_DOUBLE: {
77 double value;
78 bool result = node->GetAsDouble(&value);
79 DCHECK(result);
80 if (omit_double_type_preservation_ &&
81 value <= kint64max &&
82 value >= kint64min &&
83 std::floor(value) == value) {
84 json_string_->append(Int64ToString(static_cast<int64>(value)));
85 return result;
86 }
87 std::string real = DoubleToString(value);
88 // Ensure that the number has a .0 if there's no decimal or 'e'. This
89 // makes sure that when we read the JSON back, it's interpreted as a
90 // real rather than an int.
91 if (real.find('.') == std::string::npos &&
92 real.find('e') == std::string::npos &&
93 real.find('E') == std::string::npos) {
94 real.append(".0");
95 }
96 // The JSON spec requires that non-integer values in the range (-1,1)
97 // have a zero before the decimal point - ".52" is not valid, "0.52" is.
98 if (real[0] == '.') {
99 real.insert(static_cast<size_t>(0), static_cast<size_t>(1), '0');
100 } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
101 // "-.1" bad "-0.1" good
102 real.insert(static_cast<size_t>(1), static_cast<size_t>(1), '0');
103 }
104 json_string_->append(real);
105 return result;
106 }
107
108 case Value::TYPE_STRING: {
109 std::string value;
110 bool result = node->GetAsString(&value);
111 DCHECK(result);
112 EscapeJSONString(value, true, json_string_);
113 return result;
114 }
115
116 case Value::TYPE_LIST: {
117 json_string_->push_back('[');
118 if (pretty_print_)
119 json_string_->push_back(' ');
120
121 const ListValue* list = NULL;
122 bool first_value_has_been_output = false;
123 bool result = node->GetAsList(&list);
124 DCHECK(result);
125 for (ListValue::const_iterator it = list->begin(); it != list->end();
126 ++it) {
127 const Value* value = *it;
128 if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY)
129 continue;
130
131 if (first_value_has_been_output) {
132 json_string_->push_back(',');
133 if (pretty_print_)
134 json_string_->push_back(' ');
135 }
136
137 if (!BuildJSONString(value, depth))
138 result = false;
139
140 first_value_has_been_output = true;
141 }
142
143 if (pretty_print_)
144 json_string_->push_back(' ');
145 json_string_->push_back(']');
146 return result;
147 }
148
149 case Value::TYPE_DICTIONARY: {
150 json_string_->push_back('{');
151 if (pretty_print_)
152 json_string_->append(kPrettyPrintLineEnding);
153
154 const DictionaryValue* dict = NULL;
155 bool first_value_has_been_output = false;
156 bool result = node->GetAsDictionary(&dict);
157 DCHECK(result);
158 for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
159 itr.Advance()) {
160 if (omit_binary_values_ &&
161 itr.value().GetType() == Value::TYPE_BINARY) {
162 continue;
163 }
164
165 if (first_value_has_been_output) {
166 json_string_->push_back(',');
167 if (pretty_print_)
168 json_string_->append(kPrettyPrintLineEnding);
169 }
170
171 if (pretty_print_)
172 IndentLine(depth + 1U);
173
174 EscapeJSONString(itr.key(), true, json_string_);
175 json_string_->push_back(':');
176 if (pretty_print_)
177 json_string_->push_back(' ');
178
179 if (!BuildJSONString(&itr.value(), depth + 1U))
180 result = false;
181
182 first_value_has_been_output = true;
183 }
184
185 if (pretty_print_) {
186 json_string_->append(kPrettyPrintLineEnding);
187 IndentLine(depth);
188 }
189
190 json_string_->push_back('}');
191 return result;
192 }
193
194 case Value::TYPE_BINARY:
195 // Successful only if we're allowed to omit it.
196 DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
197 return omit_binary_values_;
198 }
199 NOTREACHED();
200 return false;
201 }
202
IndentLine(size_t depth)203 void JSONWriter::IndentLine(size_t depth) {
204 json_string_->append(depth * 3U, ' ');
205 }
206
207 } // namespace base
208