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