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