• 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/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 
16 namespace base {
17 
18 #if defined(OS_WIN)
19 static const char kPrettyPrintLineEnding[] = "\r\n";
20 #else
21 static const char kPrettyPrintLineEnding[] = "\n";
22 #endif
23 
24 // static
Write(const Value * const node,std::string * json)25 void JSONWriter::Write(const Value* const node, std::string* json) {
26   WriteWithOptions(node, 0, json);
27 }
28 
29 // static
WriteWithOptions(const Value * const node,int options,std::string * json)30 void JSONWriter::WriteWithOptions(const Value* const node, int options,
31                                   std::string* json) {
32   json->clear();
33   // Is there a better way to estimate the size of the output?
34   json->reserve(1024);
35 
36   bool omit_binary_values = !!(options & OPTIONS_OMIT_BINARY_VALUES);
37   bool omit_double_type_preservation =
38       !!(options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION);
39   bool pretty_print = !!(options & OPTIONS_PRETTY_PRINT);
40 
41   JSONWriter writer(omit_binary_values, omit_double_type_preservation,
42                     pretty_print, json);
43   writer.BuildJSONString(node, 0);
44 
45   if (pretty_print)
46     json->append(kPrettyPrintLineEnding);
47 }
48 
JSONWriter(bool omit_binary_values,bool omit_double_type_preservation,bool pretty_print,std::string * json)49 JSONWriter::JSONWriter(bool omit_binary_values,
50                        bool omit_double_type_preservation, bool pretty_print,
51                        std::string* json)
52     : omit_binary_values_(omit_binary_values),
53       omit_double_type_preservation_(omit_double_type_preservation),
54       pretty_print_(pretty_print),
55       json_string_(json) {
56   DCHECK(json);
57 }
58 
BuildJSONString(const Value * const node,int depth)59 void JSONWriter::BuildJSONString(const Value* const node, int depth) {
60   switch (node->GetType()) {
61     case Value::TYPE_NULL:
62       json_string_->append("null");
63       break;
64 
65     case Value::TYPE_BOOLEAN:
66       {
67         bool value;
68         bool result = node->GetAsBoolean(&value);
69         DCHECK(result);
70         json_string_->append(value ? "true" : "false");
71         break;
72       }
73 
74     case Value::TYPE_INTEGER:
75       {
76         int value;
77         bool result = node->GetAsInteger(&value);
78         DCHECK(result);
79         base::StringAppendF(json_string_, "%d", value);
80         break;
81       }
82 
83     case Value::TYPE_DOUBLE:
84       {
85         double value;
86         bool result = node->GetAsDouble(&value);
87         DCHECK(result);
88         if (omit_double_type_preservation_ &&
89             value <= kint64max &&
90             value >= kint64min &&
91             std::floor(value) == value) {
92           json_string_->append(Int64ToString(static_cast<int64>(value)));
93           break;
94         }
95         std::string real = DoubleToString(value);
96         // Ensure that the number has a .0 if there's no decimal or 'e'.  This
97         // makes sure that when we read the JSON back, it's interpreted as a
98         // real rather than an int.
99         if (real.find('.') == std::string::npos &&
100             real.find('e') == std::string::npos &&
101             real.find('E') == std::string::npos) {
102           real.append(".0");
103         }
104         // The JSON spec requires that non-integer values in the range (-1,1)
105         // have a zero before the decimal point - ".52" is not valid, "0.52" is.
106         if (real[0] == '.') {
107           real.insert(0, "0");
108         } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
109           // "-.1" bad "-0.1" good
110           real.insert(1, "0");
111         }
112         json_string_->append(real);
113         break;
114       }
115 
116     case Value::TYPE_STRING:
117       {
118         std::string value;
119         bool result = node->GetAsString(&value);
120         DCHECK(result);
121         EscapeJSONString(value, true, json_string_);
122         break;
123       }
124 
125     case Value::TYPE_LIST:
126       {
127         json_string_->append("[");
128         if (pretty_print_)
129           json_string_->append(" ");
130 
131         const ListValue* list = static_cast<const ListValue*>(node);
132         for (size_t i = 0; i < list->GetSize(); ++i) {
133           const Value* value = NULL;
134           bool result = list->Get(i, &value);
135           DCHECK(result);
136 
137           if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY) {
138             continue;
139           }
140 
141           if (i != 0) {
142             json_string_->append(",");
143             if (pretty_print_)
144               json_string_->append(" ");
145           }
146 
147           BuildJSONString(value, depth);
148         }
149 
150         if (pretty_print_)
151           json_string_->append(" ");
152         json_string_->append("]");
153         break;
154       }
155 
156     case Value::TYPE_DICTIONARY:
157       {
158         json_string_->append("{");
159         if (pretty_print_)
160           json_string_->append(kPrettyPrintLineEnding);
161 
162         const DictionaryValue* dict =
163             static_cast<const DictionaryValue*>(node);
164         bool first_entry = true;
165         for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
166              itr.Advance(), first_entry = false) {
167           if (omit_binary_values_ &&
168               itr.value().GetType() == Value::TYPE_BINARY) {
169             continue;
170           }
171 
172           if (!first_entry) {
173             json_string_->append(",");
174             if (pretty_print_)
175               json_string_->append(kPrettyPrintLineEnding);
176           }
177 
178           if (pretty_print_)
179             IndentLine(depth + 1);
180 
181           EscapeJSONString(itr.key(), true, json_string_);
182           if (pretty_print_) {
183             json_string_->append(": ");
184           } else {
185             json_string_->append(":");
186           }
187           BuildJSONString(&itr.value(), depth + 1);
188         }
189 
190         if (pretty_print_) {
191           json_string_->append(kPrettyPrintLineEnding);
192           IndentLine(depth);
193           json_string_->append("}");
194         } else {
195           json_string_->append("}");
196         }
197         break;
198       }
199 
200     case Value::TYPE_BINARY:
201       {
202         if (!omit_binary_values_) {
203           NOTREACHED() << "Cannot serialize binary value.";
204         }
205         break;
206       }
207 
208     default:
209       NOTREACHED() << "unknown json type";
210   }
211 }
212 
IndentLine(int depth)213 void JSONWriter::IndentLine(int depth) {
214   // It may be faster to keep an indent string so we don't have to keep
215   // reallocating.
216   json_string_->append(std::string(depth * 3, ' '));
217 }
218 
219 }  // namespace base
220