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