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