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.
5 #include "base/json/json_writer.h"
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"
18 const char kPrettyPrintLineEnding
[] = "\r\n";
20 const char kPrettyPrintLineEnding
[] = "\n";
24 bool JSONWriter::Write(const Value
* const node
, std::string
* json
) {
25 return WriteWithOptions(node
, 0, json
);
29 bool JSONWriter::WriteWithOptions(const Value
* const node
, int options
,
32 // Is there a better way to estimate the size of the output?
35 JSONWriter
writer(options
, json
);
36 bool result
= writer
.BuildJSONString(node
, 0U);
38 if (options
& OPTIONS_PRETTY_PRINT
)
39 json
->append(kPrettyPrintLineEnding
);
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),
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");
60 case Value::TYPE_BOOLEAN
: {
62 bool result
= node
->GetAsBoolean(&value
);
64 json_string_
->append(value
? "true" : "false");
68 case Value::TYPE_INTEGER
: {
70 bool result
= node
->GetAsInteger(&value
);
72 json_string_
->append(IntToString(value
));
76 case Value::TYPE_DOUBLE
: {
78 bool result
= node
->GetAsDouble(&value
);
80 if (omit_double_type_preservation_
&&
83 std::floor(value
) == value
) {
84 json_string_
->append(Int64ToString(static_cast<int64
>(value
)));
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
) {
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.
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');
104 json_string_
->append(real
);
108 case Value::TYPE_STRING
: {
110 bool result
= node
->GetAsString(&value
);
112 EscapeJSONString(value
, true, json_string_
);
116 case Value::TYPE_LIST
: {
117 json_string_
->push_back('[');
119 json_string_
->push_back(' ');
121 const ListValue
* list
= NULL
;
122 bool first_value_has_been_output
= false;
123 bool result
= node
->GetAsList(&list
);
125 for (ListValue::const_iterator it
= list
->begin(); it
!= list
->end();
127 const Value
* value
= *it
;
128 if (omit_binary_values_
&& value
->GetType() == Value::TYPE_BINARY
)
131 if (first_value_has_been_output
) {
132 json_string_
->push_back(',');
134 json_string_
->push_back(' ');
137 if (!BuildJSONString(value
, depth
))
140 first_value_has_been_output
= true;
144 json_string_
->push_back(' ');
145 json_string_
->push_back(']');
149 case Value::TYPE_DICTIONARY
: {
150 json_string_
->push_back('{');
152 json_string_
->append(kPrettyPrintLineEnding
);
154 const DictionaryValue
* dict
= NULL
;
155 bool first_value_has_been_output
= false;
156 bool result
= node
->GetAsDictionary(&dict
);
158 for (DictionaryValue::Iterator
itr(*dict
); !itr
.IsAtEnd();
160 if (omit_binary_values_
&&
161 itr
.value().GetType() == Value::TYPE_BINARY
) {
165 if (first_value_has_been_output
) {
166 json_string_
->push_back(',');
168 json_string_
->append(kPrettyPrintLineEnding
);
172 IndentLine(depth
+ 1U);
174 EscapeJSONString(itr
.key(), true, json_string_
);
175 json_string_
->push_back(':');
177 json_string_
->push_back(' ');
179 if (!BuildJSONString(&itr
.value(), depth
+ 1U))
182 first_value_has_been_output
= true;
186 json_string_
->append(kPrettyPrintLineEnding
);
190 json_string_
->push_back('}');
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_
;
203 void JSONWriter::IndentLine(size_t depth
) {
204 json_string_
->append(depth
* 3U, ' ');