xref: /third_party/gn/src/base/json/json_writer.cc (revision 6d528ed9)
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 <stdint.h>
8
9#include <cmath>
10#include <limits>
11
12#include "base/json/string_escape.h"
13#include "base/logging.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/utf_string_conversions.h"
16#include "base/values.h"
17#include "util/build_config.h"
18
19namespace base {
20
21#if defined(OS_WIN)
22const char kPrettyPrintLineEnding[] = "\r\n";
23#else
24const char kPrettyPrintLineEnding[] = "\n";
25#endif
26
27// static
28bool JSONWriter::Write(const Value& node, std::string* json) {
29  return WriteWithOptions(node, 0, json);
30}
31
32// static
33bool JSONWriter::WriteWithOptions(const Value& node,
34                                  int options,
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
40  JSONWriter writer(options, json);
41  bool result = writer.BuildJSONString(node, 0U);
42
43  if (options & OPTIONS_PRETTY_PRINT)
44    json->append(kPrettyPrintLineEnding);
45
46  return result;
47}
48
49JSONWriter::JSONWriter(int options, std::string* json)
50    : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
51      pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
52      json_string_(json) {
53  DCHECK(json);
54}
55
56bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
57  switch (node.type()) {
58    case Value::Type::NONE: {
59      json_string_->append("null");
60      return true;
61    }
62
63    case Value::Type::BOOLEAN: {
64      bool value;
65      bool result = node.GetAsBoolean(&value);
66      DCHECK(result);
67      json_string_->append(value ? "true" : "false");
68      return result;
69    }
70
71    case Value::Type::INTEGER: {
72      int value;
73      bool result = node.GetAsInteger(&value);
74      DCHECK(result);
75      json_string_->append(IntToString(value));
76      return result;
77    }
78
79    case Value::Type::STRING: {
80      std::string value;
81      bool result = node.GetAsString(&value);
82      DCHECK(result);
83      EscapeJSONString(value, true, json_string_);
84      return result;
85    }
86
87    case Value::Type::LIST: {
88      json_string_->push_back('[');
89      if (pretty_print_)
90        json_string_->push_back(' ');
91
92      const ListValue* list = nullptr;
93      bool first_value_has_been_output = false;
94      bool result = node.GetAsList(&list);
95      DCHECK(result);
96      for (const auto& value : *list) {
97        if (omit_binary_values_ && value.type() == Value::Type::BINARY)
98          continue;
99
100        if (first_value_has_been_output) {
101          json_string_->push_back(',');
102          if (pretty_print_)
103            json_string_->push_back(' ');
104        }
105
106        if (!BuildJSONString(value, depth))
107          result = false;
108
109        first_value_has_been_output = true;
110      }
111
112      if (pretty_print_)
113        json_string_->push_back(' ');
114      json_string_->push_back(']');
115      return result;
116    }
117
118    case Value::Type::DICTIONARY: {
119      json_string_->push_back('{');
120      if (pretty_print_)
121        json_string_->append(kPrettyPrintLineEnding);
122
123      const DictionaryValue* dict = nullptr;
124      bool first_value_has_been_output = false;
125      bool result = node.GetAsDictionary(&dict);
126      DCHECK(result);
127      for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
128           itr.Advance()) {
129        if (omit_binary_values_ && itr.value().type() == Value::Type::BINARY) {
130          continue;
131        }
132
133        if (first_value_has_been_output) {
134          json_string_->push_back(',');
135          if (pretty_print_)
136            json_string_->append(kPrettyPrintLineEnding);
137        }
138
139        if (pretty_print_)
140          IndentLine(depth + 1U);
141
142        EscapeJSONString(itr.key(), true, json_string_);
143        json_string_->push_back(':');
144        if (pretty_print_)
145          json_string_->push_back(' ');
146
147        if (!BuildJSONString(itr.value(), depth + 1U))
148          result = false;
149
150        first_value_has_been_output = true;
151      }
152
153      if (pretty_print_) {
154        json_string_->append(kPrettyPrintLineEnding);
155        IndentLine(depth);
156      }
157
158      json_string_->push_back('}');
159      return result;
160    }
161
162    case Value::Type::BINARY:
163      // Successful only if we're allowed to omit it.
164      DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
165      return omit_binary_values_;
166  }
167  NOTREACHED();
168  return false;
169}
170
171void JSONWriter::IndentLine(size_t depth) {
172  json_string_->append(depth * 3U, ' ');
173}
174
175}  // namespace base
176