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