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