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