blob: a95798eb98d4c70d4621a85128fa030caf33bdc5 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commitd7cae122008-07-26 21:49:384
5#include "base/json_writer.h"
6
7#include "base/logging.h"
8#include "base/string_util.h"
9#include "base/values.h"
10#include "base/string_escape.h"
11
12const char kPrettyPrintLineEnding[] = "\r\n";
13
14/* static */
[email protected]3388d4c2009-05-15 10:13:2815void JSONWriter::Write(const Value* const node,
16 bool pretty_print,
initial.commitd7cae122008-07-26 21:49:3817 std::string* json) {
[email protected]3388d4c2009-05-15 10:13:2818 WriteWithOptionalEscape(node, pretty_print, true, json);
19}
20
21/* static */
22void JSONWriter::WriteWithOptionalEscape(const Value* const node,
23 bool pretty_print,
24 bool escape,
25 std::string* json) {
initial.commitd7cae122008-07-26 21:49:3826 json->clear();
27 // Is there a better way to estimate the size of the output?
28 json->reserve(1024);
29 JSONWriter writer(pretty_print, json);
[email protected]3388d4c2009-05-15 10:13:2830 writer.BuildJSONString(node, 0, escape);
initial.commitd7cae122008-07-26 21:49:3831 if (pretty_print)
32 json->append(kPrettyPrintLineEnding);
33}
34
35JSONWriter::JSONWriter(bool pretty_print, std::string* json)
[email protected]294300a2008-08-06 15:46:4936 : json_string_(json),
37 pretty_print_(pretty_print) {
initial.commitd7cae122008-07-26 21:49:3838 DCHECK(json);
39}
40
[email protected]3388d4c2009-05-15 10:13:2841void JSONWriter::BuildJSONString(const Value* const node,
42 int depth,
43 bool escape) {
initial.commitd7cae122008-07-26 21:49:3844 switch(node->GetType()) {
45 case Value::TYPE_NULL:
46 json_string_->append("null");
47 break;
48
49 case Value::TYPE_BOOLEAN:
50 {
51 bool value;
52 bool result = node->GetAsBoolean(&value);
53 DCHECK(result);
54 json_string_->append(value ? "true" : "false");
55 break;
56 }
57
58 case Value::TYPE_INTEGER:
59 {
60 int value;
61 bool result = node->GetAsInteger(&value);
62 DCHECK(result);
63 StringAppendF(json_string_, "%d", value);
64 break;
65 }
66
67 case Value::TYPE_REAL:
68 {
69 double value;
70 bool result = node->GetAsReal(&value);
71 DCHECK(result);
[email protected]6dce8ade2008-11-18 00:14:2872 std::string real = DoubleToString(value);
initial.commitd7cae122008-07-26 21:49:3873 // Ensure that the number has a .0 if there's no decimal or 'e'. This
74 // makes sure that when we read the JSON back, it's interpreted as a
75 // real rather than an int.
76 if (real.find('.') == std::string::npos &&
77 real.find('e') == std::string::npos &&
78 real.find('E') == std::string::npos) {
79 real.append(".0");
80 }
[email protected]bcf60bb2009-04-20 19:14:4381 // The JSON spec requires that non-integer values in the range (-1,1)
82 // have a zero before the decimal point - ".52" is not valid, "0.52" is.
83 if (real[0] == '.') {
84 real.insert(0, "0");
85 } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
86 // "-.1" bad "-0.1" good
87 real.insert(1, "0");
88 }
initial.commitd7cae122008-07-26 21:49:3889 json_string_->append(real);
90 break;
91 }
92
93 case Value::TYPE_STRING:
94 {
[email protected]d36519b2009-05-20 16:43:4995 std::string value;
96 bool result = node->GetAsString(&value);
97 DCHECK(result);
[email protected]3388d4c2009-05-15 10:13:2898 if (escape) {
[email protected]d36519b2009-05-20 16:43:4999 string_escape::JsonDoubleQuote(UTF8ToUTF16(value),
100 true,
101 json_string_);
[email protected]3388d4c2009-05-15 10:13:28102 } else {
[email protected]d36519b2009-05-20 16:43:49103 string_escape::JsonDoubleQuote(value, true, json_string_);
[email protected]3388d4c2009-05-15 10:13:28104 }
initial.commitd7cae122008-07-26 21:49:38105 break;
106 }
107
108 case Value::TYPE_LIST:
109 {
110 json_string_->append("[");
111 if (pretty_print_)
112 json_string_->append(" ");
113
114 const ListValue* list = static_cast<const ListValue*>(node);
115 for (size_t i = 0; i < list->GetSize(); ++i) {
116 if (i != 0) {
117 json_string_->append(",");
118 if (pretty_print_)
119 json_string_->append(" ");
120 }
121
122 Value* value = NULL;
123 bool result = list->Get(i, &value);
124 DCHECK(result);
[email protected]3388d4c2009-05-15 10:13:28125 BuildJSONString(value, depth, escape);
initial.commitd7cae122008-07-26 21:49:38126 }
127
128 if (pretty_print_)
129 json_string_->append(" ");
130 json_string_->append("]");
131 break;
132 }
133
134 case Value::TYPE_DICTIONARY:
135 {
136 json_string_->append("{");
137 if (pretty_print_)
138 json_string_->append(kPrettyPrintLineEnding);
139
140 const DictionaryValue* dict =
141 static_cast<const DictionaryValue*>(node);
142 for (DictionaryValue::key_iterator key_itr = dict->begin_keys();
143 key_itr != dict->end_keys();
144 ++key_itr) {
145
146 if (key_itr != dict->begin_keys()) {
147 json_string_->append(",");
148 if (pretty_print_)
149 json_string_->append(kPrettyPrintLineEnding);
150 }
151
152 Value* value = NULL;
153 bool result = dict->Get(*key_itr, &value);
154 DCHECK(result);
155
156 if (pretty_print_)
157 IndentLine(depth + 1);
158 AppendQuotedString(*key_itr);
159 if (pretty_print_) {
160 json_string_->append(": ");
161 } else {
162 json_string_->append(":");
163 }
[email protected]3388d4c2009-05-15 10:13:28164 BuildJSONString(value, depth + 1, escape);
initial.commitd7cae122008-07-26 21:49:38165 }
166
167 if (pretty_print_) {
168 json_string_->append(kPrettyPrintLineEnding);
169 IndentLine(depth);
170 json_string_->append("}");
171 } else {
172 json_string_->append("}");
173 }
174 break;
175 }
176
177 default:
178 // TODO(jhughes): handle TYPE_BINARY
179 NOTREACHED() << "unknown json type";
180 }
181}
182
[email protected]8e50b602009-03-03 22:59:43183void JSONWriter::AppendQuotedString(const std::wstring& str) {
[email protected]d36519b2009-05-20 16:43:49184 string_escape::JsonDoubleQuote(WideToUTF16Hack(str),
185 true,
186 json_string_);
initial.commitd7cae122008-07-26 21:49:38187}
188
189void JSONWriter::IndentLine(int depth) {
190 // It may be faster to keep an indent string so we don't have to keep
191 // reallocating.
192 json_string_->append(std::string(depth * 3, ' '));
193}