blob: 19f28f21ada24ff0416a5ef4d4919796b0e76e05 [file] [log] [blame]
kkania@chromium.org31679d92012-03-09 07:55:29 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botf003cfe2008-08-24 09:55:55 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit3f4a7322008-07-27 06:49:38 +09004
brettw@chromium.org7cd41eb2009-10-24 05:00:20 +09005#include "base/json/json_writer.h"
initial.commit3f4a7322008-07-27 06:49:38 +09006
avi5853c892015-12-04 01:46:54 +09007#include <stdint.h>
8
kkania@chromium.org31679d92012-03-09 07:55:29 +09009#include <cmath>
avi5853c892015-12-04 01:46:54 +090010#include <limits>
kkania@chromium.org31679d92012-03-09 07:55:29 +090011
brettw@chromium.org7cd41eb2009-10-24 05:00:20 +090012#include "base/json/string_escape.h"
initial.commit3f4a7322008-07-27 06:49:38 +090013#include "base/logging.h"
brettw@chromium.orgabcde5c2013-02-07 11:57:22 +090014#include "base/strings/string_number_conversions.h"
avi@chromium.org17f60622013-06-08 03:37:07 +090015#include "base/strings/utf_string_conversions.h"
tfarina@chromium.org4af74f52013-03-21 01:50:32 +090016#include "base/values.h"
avia6a6a682015-12-27 07:15:14 +090017#include "build/build_config.h"
initial.commit3f4a7322008-07-27 06:49:38 +090018
brettw@chromium.org7cd41eb2009-10-24 05:00:20 +090019namespace base {
20
mark@chromium.org95c9ec92009-06-27 06:17:24 +090021#if defined(OS_WIN)
gab@chromium.orgedbdd402014-01-30 07:50:28 +090022const char kPrettyPrintLineEnding[] = "\r\n";
mark@chromium.org95c9ec92009-06-27 06:17:24 +090023#else
gab@chromium.orgedbdd402014-01-30 07:50:28 +090024const char kPrettyPrintLineEnding[] = "\n";
mark@chromium.org95c9ec92009-06-27 06:17:24 +090025#endif
initial.commit3f4a7322008-07-27 06:49:38 +090026
rsesek@chromium.orgdc66ca72013-12-12 07:10:45 +090027// static
estadeb5f30dd2015-05-16 10:02:34 +090028bool JSONWriter::Write(const Value& node, std::string* json) {
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +090029 return WriteWithOptions(node, 0, json);
pfeldman@chromium.orgf68146b2009-05-15 19:13:28 +090030}
31
rsesek@chromium.orgdc66ca72013-12-12 07:10:45 +090032// static
estadeb5f30dd2015-05-16 10:02:34 +090033bool JSONWriter::WriteWithOptions(const Value& node,
34 int options,
ericdingle@chromium.orgdfb9b252011-11-18 11:29:54 +090035 std::string* json) {
initial.commit3f4a7322008-07-27 06:49:38 +090036 json->clear();
37 // Is there a better way to estimate the size of the output?
38 json->reserve(1024);
ericdingle@chromium.org3cfd0162012-03-16 10:59:55 +090039
gab@chromium.orgedbdd402014-01-30 07:50:28 +090040 JSONWriter writer(options, json);
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +090041 bool result = writer.BuildJSONString(node, 0U);
ericdingle@chromium.org3cfd0162012-03-16 10:59:55 +090042
gab@chromium.orgedbdd402014-01-30 07:50:28 +090043 if (options & OPTIONS_PRETTY_PRINT)
initial.commit3f4a7322008-07-27 06:49:38 +090044 json->append(kPrettyPrintLineEnding);
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +090045
46 return result;
initial.commit3f4a7322008-07-27 06:49:38 +090047}
48
gab@chromium.orgedbdd402014-01-30 07:50:28 +090049JSONWriter::JSONWriter(int options, std::string* json)
50 : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
51 omit_double_type_preservation_(
52 (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0),
53 pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
ericdingle@chromium.org3cfd0162012-03-16 10:59:55 +090054 json_string_(json) {
initial.commit3f4a7322008-07-27 06:49:38 +090055 DCHECK(json);
56}
57
estadeb5f30dd2015-05-16 10:02:34 +090058bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
59 switch (node.GetType()) {
jdoerrie89ee31a2016-12-08 00:43:28 +090060 case Value::Type::NONE: {
initial.commit3f4a7322008-07-27 06:49:38 +090061 json_string_->append("null");
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +090062 return true;
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090063 }
initial.commit3f4a7322008-07-27 06:49:38 +090064
jdoerrie89ee31a2016-12-08 00:43:28 +090065 case Value::Type::BOOLEAN: {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090066 bool value;
estadeb5f30dd2015-05-16 10:02:34 +090067 bool result = node.GetAsBoolean(&value);
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090068 DCHECK(result);
69 json_string_->append(value ? "true" : "false");
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +090070 return result;
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090071 }
72
jdoerrie89ee31a2016-12-08 00:43:28 +090073 case Value::Type::INTEGER: {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090074 int value;
estadeb5f30dd2015-05-16 10:02:34 +090075 bool result = node.GetAsInteger(&value);
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090076 DCHECK(result);
77 json_string_->append(IntToString(value));
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +090078 return result;
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090079 }
80
jdoerrie89ee31a2016-12-08 00:43:28 +090081 case Value::Type::DOUBLE: {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090082 double value;
estadeb5f30dd2015-05-16 10:02:34 +090083 bool result = node.GetAsDouble(&value);
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090084 DCHECK(result);
85 if (omit_double_type_preservation_ &&
avi5853c892015-12-04 01:46:54 +090086 value <= std::numeric_limits<int64_t>::max() &&
87 value >= std::numeric_limits<int64_t>::min() &&
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090088 std::floor(value) == value) {
avi5853c892015-12-04 01:46:54 +090089 json_string_->append(Int64ToString(static_cast<int64_t>(value)));
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +090090 return result;
initial.commit3f4a7322008-07-27 06:49:38 +090091 }
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +090092 std::string real = DoubleToString(value);
93 // Ensure that the number has a .0 if there's no decimal or 'e'. This
94 // makes sure that when we read the JSON back, it's interpreted as a
95 // real rather than an int.
96 if (real.find('.') == std::string::npos &&
97 real.find('e') == std::string::npos &&
98 real.find('E') == std::string::npos) {
99 real.append(".0");
initial.commit3f4a7322008-07-27 06:49:38 +0900100 }
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900101 // The JSON spec requires that non-integer values in the range (-1,1)
102 // have a zero before the decimal point - ".52" is not valid, "0.52" is.
103 if (real[0] == '.') {
yang.gu@intel.com103973a2014-03-01 00:25:31 +0900104 real.insert(static_cast<size_t>(0), static_cast<size_t>(1), '0');
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900105 } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
106 // "-.1" bad "-0.1" good
yang.gu@intel.com103973a2014-03-01 00:25:31 +0900107 real.insert(static_cast<size_t>(1), static_cast<size_t>(1), '0');
initial.commit3f4a7322008-07-27 06:49:38 +0900108 }
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900109 json_string_->append(real);
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900110 return result;
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900111 }
initial.commit3f4a7322008-07-27 06:49:38 +0900112
jdoerrie89ee31a2016-12-08 00:43:28 +0900113 case Value::Type::STRING: {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900114 std::string value;
estadeb5f30dd2015-05-16 10:02:34 +0900115 bool result = node.GetAsString(&value);
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900116 DCHECK(result);
117 EscapeJSONString(value, true, json_string_);
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900118 return result;
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900119 }
initial.commit3f4a7322008-07-27 06:49:38 +0900120
jdoerrie89ee31a2016-12-08 00:43:28 +0900121 case Value::Type::LIST: {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900122 json_string_->push_back('[');
123 if (pretty_print_)
124 json_string_->push_back(' ');
initial.commit3f4a7322008-07-27 06:49:38 +0900125
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900126 const ListValue* list = NULL;
127 bool first_value_has_been_output = false;
estadeb5f30dd2015-05-16 10:02:34 +0900128 bool result = node.GetAsList(&list);
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900129 DCHECK(result);
dcheng1fa44fb2016-05-26 03:30:47 +0900130 for (const auto& value : *list) {
jdoerrie45184002017-04-12 03:09:14 +0900131 if (omit_binary_values_ && value.GetType() == Value::Type::BINARY)
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900132 continue;
ericdingle@chromium.orgdfb9b252011-11-18 11:29:54 +0900133
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900134 if (first_value_has_been_output) {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900135 json_string_->push_back(',');
gab@chromium.orgedbdd402014-01-30 07:50:28 +0900136 if (pretty_print_)
137 json_string_->push_back(' ');
initial.commit3f4a7322008-07-27 06:49:38 +0900138 }
139
jdoerrie45184002017-04-12 03:09:14 +0900140 if (!BuildJSONString(value, depth))
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900141 result = false;
142
143 first_value_has_been_output = true;
initial.commit3f4a7322008-07-27 06:49:38 +0900144 }
145
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900146 if (pretty_print_)
147 json_string_->push_back(' ');
148 json_string_->push_back(']');
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900149 return result;
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900150 }
151
jdoerrie89ee31a2016-12-08 00:43:28 +0900152 case Value::Type::DICTIONARY: {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900153 json_string_->push_back('{');
154 if (pretty_print_)
155 json_string_->append(kPrettyPrintLineEnding);
156
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900157 const DictionaryValue* dict = NULL;
158 bool first_value_has_been_output = false;
estadeb5f30dd2015-05-16 10:02:34 +0900159 bool result = node.GetAsDictionary(&dict);
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900160 DCHECK(result);
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900161 for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900162 itr.Advance()) {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900163 if (omit_binary_values_ &&
jdoerrie89ee31a2016-12-08 00:43:28 +0900164 itr.value().GetType() == Value::Type::BINARY) {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900165 continue;
ericdingle@chromium.orgdfb9b252011-11-18 11:29:54 +0900166 }
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900167
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900168 if (first_value_has_been_output) {
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900169 json_string_->push_back(',');
170 if (pretty_print_)
171 json_string_->append(kPrettyPrintLineEnding);
172 }
173
174 if (pretty_print_)
175 IndentLine(depth + 1U);
176
177 EscapeJSONString(itr.key(), true, json_string_);
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900178 json_string_->push_back(':');
179 if (pretty_print_)
180 json_string_->push_back(' ');
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900181
estadeb5f30dd2015-05-16 10:02:34 +0900182 if (!BuildJSONString(itr.value(), depth + 1U))
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900183 result = false;
184
185 first_value_has_been_output = true;
ericdingle@chromium.orgdfb9b252011-11-18 11:29:54 +0900186 }
187
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900188 if (pretty_print_) {
189 json_string_->append(kPrettyPrintLineEnding);
190 IndentLine(depth);
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900191 }
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900192
193 json_string_->push_back('}');
194 return result;
tsepez@chromium.org1d7a4c42014-02-06 18:24:48 +0900195 }
196
jdoerrie89ee31a2016-12-08 00:43:28 +0900197 case Value::Type::BINARY:
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900198 // Successful only if we're allowed to omit it.
199 DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
200 return omit_binary_values_;
initial.commit3f4a7322008-07-27 06:49:38 +0900201 }
tsepez@chromium.org71ec72b2014-02-08 07:47:39 +0900202 NOTREACHED();
203 return false;
initial.commit3f4a7322008-07-27 06:49:38 +0900204}
205
gab@chromium.orgedbdd402014-01-30 07:50:28 +0900206void JSONWriter::IndentLine(size_t depth) {
207 json_string_->append(depth * 3U, ' ');
initial.commit3f4a7322008-07-27 06:49:38 +0900208}
brettw@chromium.org7cd41eb2009-10-24 05:00:20 +0900209
210} // namespace base