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 15namespace base { 16 17#if defined(OS_WIN) 18const char kPrettyPrintLineEnding[] = "\r\n"; 19#else 20const char kPrettyPrintLineEnding[] = "\n"; 21#endif 22 23// static 24bool JSONWriter::Write(const Value* const node, std::string* json) { 25 return WriteWithOptions(node, 0, json); 26} 27 28// static 29bool 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 44JSONWriter::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 53bool 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 203void JSONWriter::IndentLine(size_t depth) { 204 json_string_->append(depth * 3U, ' '); 205} 206 207} // namespace base 208