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 <stdint.h>
8
9 #include <cmath>
10 #include <limits>
11
12 #include "base/json/string_escape.h"
13 #include "base/logging.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "util/build_config.h"
18
19 namespace base {
20
21 #if defined(OS_WIN)
22 const char kPrettyPrintLineEnding[] = "\r\n";
23 #else
24 const char kPrettyPrintLineEnding[] = "\n";
25 #endif
26
27 // static
Write(const Value& node, std::string* json)28 bool JSONWriter::Write(const Value& node, std::string* json) {
29 return WriteWithOptions(node, 0, json);
30 }
31
32 // static
WriteWithOptions(const Value& node, int options, std::string* json)33 bool 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
JSONWriter(int options, std::string* json)49 JSONWriter::JSONWriter(int options, std::string* json)
50 : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
51 pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
52 json_string_(json) {
53 DCHECK(json);
54 }
55
BuildJSONString(const Value& node, size_t depth)56 bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
57 switch (node.type()) {
58 case Value::Type::NONE: {
59 json_string_->append("null");
60 return true;
61 }
62
63 case Value::Type::BOOLEAN: {
64 bool value;
65 bool result = node.GetAsBoolean(&value);
66 DCHECK(result);
67 json_string_->append(value ? "true" : "false");
68 return result;
69 }
70
71 case Value::Type::INTEGER: {
72 int value;
73 bool result = node.GetAsInteger(&value);
74 DCHECK(result);
75 json_string_->append(IntToString(value));
76 return result;
77 }
78
79 case Value::Type::STRING: {
80 std::string value;
81 bool result = node.GetAsString(&value);
82 DCHECK(result);
83 EscapeJSONString(value, true, json_string_);
84 return result;
85 }
86
87 case Value::Type::LIST: {
88 json_string_->push_back('[');
89 if (pretty_print_)
90 json_string_->push_back(' ');
91
92 const ListValue* list = nullptr;
93 bool first_value_has_been_output = false;
94 bool result = node.GetAsList(&list);
95 DCHECK(result);
96 for (const auto& value : *list) {
97 if (omit_binary_values_ && value.type() == Value::Type::BINARY)
98 continue;
99
100 if (first_value_has_been_output) {
101 json_string_->push_back(',');
102 if (pretty_print_)
103 json_string_->push_back(' ');
104 }
105
106 if (!BuildJSONString(value, depth))
107 result = false;
108
109 first_value_has_been_output = true;
110 }
111
112 if (pretty_print_)
113 json_string_->push_back(' ');
114 json_string_->push_back(']');
115 return result;
116 }
117
118 case Value::Type::DICTIONARY: {
119 json_string_->push_back('{');
120 if (pretty_print_)
121 json_string_->append(kPrettyPrintLineEnding);
122
123 const DictionaryValue* dict = nullptr;
124 bool first_value_has_been_output = false;
125 bool result = node.GetAsDictionary(&dict);
126 DCHECK(result);
127 for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
128 itr.Advance()) {
129 if (omit_binary_values_ && itr.value().type() == Value::Type::BINARY) {
130 continue;
131 }
132
133 if (first_value_has_been_output) {
134 json_string_->push_back(',');
135 if (pretty_print_)
136 json_string_->append(kPrettyPrintLineEnding);
137 }
138
139 if (pretty_print_)
140 IndentLine(depth + 1U);
141
142 EscapeJSONString(itr.key(), true, json_string_);
143 json_string_->push_back(':');
144 if (pretty_print_)
145 json_string_->push_back(' ');
146
147 if (!BuildJSONString(itr.value(), depth + 1U))
148 result = false;
149
150 first_value_has_been_output = true;
151 }
152
153 if (pretty_print_) {
154 json_string_->append(kPrettyPrintLineEnding);
155 IndentLine(depth);
156 }
157
158 json_string_->push_back('}');
159 return result;
160 }
161
162 case Value::Type::BINARY:
163 // Successful only if we're allowed to omit it.
164 DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
165 return omit_binary_values_;
166 }
167 NOTREACHED();
168 return false;
169 }
170
IndentLine(size_t depth)171 void JSONWriter::IndentLine(size_t depth) {
172 json_string_->append(depth * 3U, ' ');
173 }
174
175 } // namespace base
176