• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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