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