| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/json/json_writer.h" |
| |
| #include <stdint.h> |
| |
| #include <cmath> |
| #include <limits> |
| |
| #include "base/json/string_escape.h" |
| #include "base/logging.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| |
| namespace base { |
| |
| #if BUILDFLAG(IS_WIN) |
| const char kPrettyPrintLineEnding[] = "\r\n"; |
| #else |
| const char kPrettyPrintLineEnding[] = "\n"; |
| #endif |
| |
| // static |
| bool JSONWriter::Write(ValueView node, std::string* json, size_t max_depth) { |
| return WriteWithOptions(node, 0, json, max_depth); |
| } |
| |
| // static |
| bool JSONWriter::WriteWithOptions(ValueView node, |
| int options, |
| std::string* json, |
| size_t max_depth) { |
| json->clear(); |
| // Is there a better way to estimate the size of the output? |
| if (json->capacity() < 1024) { |
| json->reserve(1024); |
| } |
| |
| JSONWriter writer(options, json, max_depth); |
| bool result = node.Visit([&writer](const auto& member) { |
| return writer.BuildJSONString(member, 0); |
| }); |
| |
| if (options & OPTIONS_PRETTY_PRINT) { |
| json->append(kPrettyPrintLineEnding); |
| } |
| |
| return result; |
| } |
| |
| JSONWriter::JSONWriter(int options, std::string* json, size_t max_depth) |
| : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0), |
| omit_double_type_preservation_( |
| (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0), |
| pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0), |
| json_string_(json), |
| max_depth_(max_depth), |
| stack_depth_(0) { |
| DCHECK(json); |
| CHECK_LE(max_depth, internal::kAbsoluteMaxDepth); |
| } |
| |
| bool JSONWriter::BuildJSONString(absl::monostate node, size_t depth) { |
| json_string_->append("null"); |
| return true; |
| } |
| |
| bool JSONWriter::BuildJSONString(bool node, size_t depth) { |
| json_string_->append(node ? "true" : "false"); |
| return true; |
| } |
| |
| bool JSONWriter::BuildJSONString(int node, size_t depth) { |
| json_string_->append(NumberToString(node)); |
| return true; |
| } |
| |
| bool JSONWriter::BuildJSONString(double node, size_t depth) { |
| if (omit_double_type_preservation_ && |
| IsValueInRangeForNumericType<int64_t>(node) && std::floor(node) == node) { |
| json_string_->append(NumberToString(static_cast<int64_t>(node))); |
| return true; |
| } |
| |
| std::string real = NumberToString(node); |
| // Ensure that the number has a .0 if there's no decimal or 'e'. This |
| // makes sure that when we read the JSON back, it's interpreted as a |
| // real rather than an int. |
| if (real.find_first_of(".eE") == std::string::npos) { |
| real.append(".0"); |
| } |
| |
| // The JSON spec requires that non-integer values in the range (-1,1) |
| // have a zero before the decimal point - ".52" is not valid, "0.52" is. |
| if (real[0] == '.') { |
| real.insert(0, 1, '0'); |
| } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { |
| // "-.1" bad "-0.1" good |
| real.insert(1, 1, '0'); |
| } |
| json_string_->append(real); |
| return true; |
| } |
| |
| bool JSONWriter::BuildJSONString(StringPiece node, size_t depth) { |
| EscapeJSONString(node, true, json_string_); |
| return true; |
| } |
| |
| bool JSONWriter::BuildJSONString(const Value::BlobStorage& node, size_t depth) { |
| // Successful only if we're allowed to omit it. |
| DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value."; |
| return omit_binary_values_; |
| } |
| |
| bool JSONWriter::BuildJSONString(const Value::Dict& node, size_t depth) { |
| internal::StackMarker depth_check(max_depth_, &stack_depth_); |
| |
| if (depth_check.IsTooDeep()) { |
| return false; |
| } |
| |
| json_string_->push_back('{'); |
| if (pretty_print_) { |
| json_string_->append(kPrettyPrintLineEnding); |
| } |
| |
| bool first_value_has_been_output = false; |
| bool result = true; |
| for (const auto [key, value] : node) { |
| if (omit_binary_values_ && value.type() == Value::Type::BINARY) { |
| continue; |
| } |
| |
| if (first_value_has_been_output) { |
| json_string_->push_back(','); |
| if (pretty_print_) { |
| json_string_->append(kPrettyPrintLineEnding); |
| } |
| } |
| |
| if (pretty_print_) { |
| IndentLine(depth + 1U); |
| } |
| |
| EscapeJSONString(key, true, json_string_); |
| json_string_->push_back(':'); |
| if (pretty_print_) { |
| json_string_->push_back(' '); |
| } |
| |
| result &= value.Visit([this, depth = depth + 1](const auto& member) { |
| return BuildJSONString(member, depth); |
| }); |
| |
| first_value_has_been_output = true; |
| } |
| |
| if (pretty_print_) { |
| if (first_value_has_been_output) { |
| json_string_->append(kPrettyPrintLineEnding); |
| } |
| IndentLine(depth); |
| } |
| |
| json_string_->push_back('}'); |
| return result; |
| } |
| |
| bool JSONWriter::BuildJSONString(const Value::List& node, size_t depth) { |
| internal::StackMarker depth_check(max_depth_, &stack_depth_); |
| |
| if (depth_check.IsTooDeep()) { |
| return false; |
| } |
| |
| json_string_->push_back('['); |
| if (pretty_print_) { |
| json_string_->push_back(' '); |
| } |
| |
| bool first_value_has_been_output = false; |
| bool result = true; |
| for (const auto& value : node) { |
| if (omit_binary_values_ && value.type() == Value::Type::BINARY) { |
| continue; |
| } |
| |
| if (first_value_has_been_output) { |
| json_string_->push_back(','); |
| if (pretty_print_) { |
| json_string_->push_back(' '); |
| } |
| } |
| |
| result &= value.Visit([this, depth](const auto& member) { |
| return BuildJSONString(member, depth); |
| }); |
| |
| first_value_has_been_output = true; |
| } |
| |
| if (pretty_print_) { |
| json_string_->push_back(' '); |
| } |
| json_string_->push_back(']'); |
| return result; |
| } |
| |
| void JSONWriter::IndentLine(size_t depth) { |
| json_string_->append(depth * 3U, ' '); |
| } |
| |
| absl::optional<std::string> WriteJson(ValueView node, size_t max_depth) { |
| std::string result; |
| if (!JSONWriter::Write(node, &result, max_depth)) { |
| return absl::nullopt; |
| } |
| return result; |
| } |
| |
| absl::optional<std::string> WriteJsonWithOptions(ValueView node, |
| uint32_t options, |
| size_t max_depth) { |
| std::string result; |
| if (!JSONWriter::WriteWithOptions(node, static_cast<int>(options), &result, |
| max_depth)) { |
| return absl::nullopt; |
| } |
| return result; |
| } |
| |
| } // namespace base |