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 16namespace base { 17 18#if defined(OS_WIN) 19static const char kPrettyPrintLineEnding[] = "\r\n"; 20#else 21static const char kPrettyPrintLineEnding[] = "\n"; 22#endif 23 24// static 25void JSONWriter::Write(const Value* const node, std::string* json) { 26 WriteWithOptions(node, 0, json); 27} 28 29// static 30void 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 49JSONWriter::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 59void 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 213void 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