| Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
| license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 4 | |
| [email protected] | 93d49d7 | 2009-10-23 20:00:20 | [diff] [blame] | 5 | #include "base/json/json_writer.h" |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 6 | |
| avi | 7e3976a | 2015-12-03 16:46:54 | [diff] [blame] | 7 | #include <stdint.h> |
| 8 | |
| [email protected] | ba4fb5a | 2012-03-08 22:55:29 | [diff] [blame] | 9 | #include <cmath> |
| avi | 7e3976a | 2015-12-03 16:46:54 | [diff] [blame] | 10 | #include <limits> |
| Helmut Januschka | 4e477ef4 | 2024-02-27 19:26:45 | [diff] [blame] | 11 | #include <string_view> |
| Victor Hugo Vianna Silva | ab2c6ac | 2025-03-18 19:52:34 | [diff] [blame] | 12 | #include <variant> |
| [email protected] | ba4fb5a | 2012-03-08 22:55:29 | [diff] [blame] | 13 | |
| [email protected] | 93d49d7 | 2009-10-23 20:00:20 | [diff] [blame] | 14 | #include "base/json/string_escape.h" |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 15 | #include "base/logging.h" |
| Peter Kasting | fbb9e56 | 2021-06-27 02:57:37 | [diff] [blame] | 16 | #include "base/numerics/safe_conversions.h" |
| [email protected] | dfa049e | 2013-02-07 02:57:22 | [diff] [blame] | 17 | #include "base/strings/string_number_conversions.h" |
| Devon Loehr | aebcaea4 | 2025-03-05 22:12:32 | [diff] [blame] | 18 | #include "base/strings/to_string.h" |
| [email protected] | cf0e622 | 2013-03-20 16:50:32 | [diff] [blame] | 19 | #include "base/values.h" |
| avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 20 | #include "build/build_config.h" |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 21 | |
| [email protected] | 93d49d7 | 2009-10-23 20:00:20 | [diff] [blame] | 22 | namespace base { |
| 23 | |
| Xiaohan Wang | 01bac02 | 2022-01-15 14:45:01 | [diff] [blame] | 24 | #if BUILDFLAG(IS_WIN) |
| [email protected] | 44ea531 | 2014-01-29 22:50:28 | [diff] [blame] | 25 | const char kPrettyPrintLineEnding[] = "\r\n"; |
| [email protected] | b81637c3 | 2009-06-26 21:17:24 | [diff] [blame] | 26 | #else |
| [email protected] | 44ea531 | 2014-01-29 22:50:28 | [diff] [blame] | 27 | const char kPrettyPrintLineEnding[] = "\n"; |
| [email protected] | b81637c3 | 2009-06-26 21:17:24 | [diff] [blame] | 28 | #endif |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 29 | |
| [email protected] | bbe1571 | 2013-12-11 22:10:45 | [diff] [blame] | 30 | // static |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 31 | bool JSONWriter::Write(ValueView node, std::string* json, size_t max_depth) { |
| Chris Davis | 3dece34 | 2019-10-09 00:42:13 | [diff] [blame] | 32 | return WriteWithOptions(node, 0, json, max_depth); |
| [email protected] | 3388d4c | 2009-05-15 10:13:28 | [diff] [blame] | 33 | } |
| 34 | |
| [email protected] | bbe1571 | 2013-12-11 22:10:45 | [diff] [blame] | 35 | // static |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 36 | bool JSONWriter::WriteWithOptions(ValueView node, |
| estade | 8d04646 | 2015-05-16 01:02:34 | [diff] [blame] | 37 | int options, |
| Chris Davis | 3dece34 | 2019-10-09 00:42:13 | [diff] [blame] | 38 | std::string* json, |
| 39 | size_t max_depth) { |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 40 | json->clear(); |
| 41 | // Is there a better way to estimate the size of the output? |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 42 | if (json->capacity() < 1024) { |
| Dominic Battre | c75c585 | 2021-02-12 23:14:46 | [diff] [blame] | 43 | json->reserve(1024); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 44 | } |
| [email protected] | 4abb460 | 2012-03-16 01:59:55 | [diff] [blame] | 45 | |
| Chris Davis | 3dece34 | 2019-10-09 00:42:13 | [diff] [blame] | 46 | JSONWriter writer(options, json, max_depth); |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 47 | bool result = node.Visit([&writer](const auto& member) { |
| 48 | return writer.BuildJSONString(member, 0); |
| 49 | }); |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 50 | |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 51 | if (options & OPTIONS_PRETTY_PRINT) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 52 | json->append(kPrettyPrintLineEnding); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 53 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 54 | |
| 55 | return result; |
| 56 | } |
| 57 | |
| Chris Davis | 3dece34 | 2019-10-09 00:42:13 | [diff] [blame] | 58 | JSONWriter::JSONWriter(int options, std::string* json, size_t max_depth) |
| [email protected] | 44ea531 | 2014-01-29 22:50:28 | [diff] [blame] | 59 | : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0), |
| 60 | omit_double_type_preservation_( |
| 61 | (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0), |
| 62 | pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0), |
| Chris Davis | 3dece34 | 2019-10-09 00:42:13 | [diff] [blame] | 63 | json_string_(json), |
| 64 | max_depth_(max_depth), |
| 65 | stack_depth_(0) { |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 66 | DCHECK(json); |
| Chris Davis | 3dece34 | 2019-10-09 00:42:13 | [diff] [blame] | 67 | CHECK_LE(max_depth, internal::kAbsoluteMaxDepth); |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 68 | } |
| 69 | |
| Victor Hugo Vianna Silva | ab2c6ac | 2025-03-18 19:52:34 | [diff] [blame] | 70 | bool JSONWriter::BuildJSONString(std::monostate node, size_t depth) { |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 71 | json_string_->append("null"); |
| 72 | return true; |
| 73 | } |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 74 | |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 75 | bool JSONWriter::BuildJSONString(bool node, size_t depth) { |
| Devon Loehr | aebcaea4 | 2025-03-05 22:12:32 | [diff] [blame] | 76 | json_string_->append(base::ToString(node)); |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 77 | return true; |
| 78 | } |
| [email protected] | e959b8dc | 2014-02-06 09:24:48 | [diff] [blame] | 79 | |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 80 | bool JSONWriter::BuildJSONString(int node, size_t depth) { |
| 81 | json_string_->append(NumberToString(node)); |
| 82 | return true; |
| 83 | } |
| [email protected] | e959b8dc | 2014-02-06 09:24:48 | [diff] [blame] | 84 | |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 85 | bool JSONWriter::BuildJSONString(double node, size_t depth) { |
| 86 | if (omit_double_type_preservation_ && |
| 87 | IsValueInRangeForNumericType<int64_t>(node) && std::floor(node) == node) { |
| 88 | json_string_->append(NumberToString(static_cast<int64_t>(node))); |
| 89 | return true; |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 90 | } |
| jdoerrie | fdea8be | 2018-10-30 22:15:30 | [diff] [blame] | 91 | |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 92 | std::string real = NumberToString(node); |
| 93 | // Ensure that the number has a .0 if there's no decimal or 'e'. This |
| 94 | // makes sure that when we read the JSON back, it's interpreted as a |
| 95 | // real rather than an int. |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 96 | if (real.find_first_of(".eE") == std::string::npos) { |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 97 | real.append(".0"); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 98 | } |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 99 | |
| 100 | // The JSON spec requires that non-integer values in the range (-1,1) |
| 101 | // have a zero before the decimal point - ".52" is not valid, "0.52" is. |
| 102 | if (real[0] == '.') { |
| 103 | real.insert(0, 1, '0'); |
| 104 | } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { |
| 105 | // "-.1" bad "-0.1" good |
| 106 | real.insert(1, 1, '0'); |
| 107 | } |
| 108 | json_string_->append(real); |
| 109 | return true; |
| 110 | } |
| 111 | |
| Helmut Januschka | 4e477ef4 | 2024-02-27 19:26:45 | [diff] [blame] | 112 | bool JSONWriter::BuildJSONString(std::string_view node, size_t depth) { |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 113 | EscapeJSONString(node, true, json_string_); |
| 114 | return true; |
| 115 | } |
| 116 | |
| 117 | bool JSONWriter::BuildJSONString(const Value::BlobStorage& node, size_t depth) { |
| 118 | // Successful only if we're allowed to omit it. |
| 119 | DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value."; |
| 120 | return omit_binary_values_; |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 121 | } |
| 122 | |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 123 | bool JSONWriter::BuildJSONString(const Value::Dict& node, size_t depth) { |
| 124 | internal::StackMarker depth_check(max_depth_, &stack_depth_); |
| 125 | |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 126 | if (depth_check.IsTooDeep()) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 127 | return false; |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 128 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 129 | |
| 130 | json_string_->push_back('{'); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 131 | if (pretty_print_) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 132 | json_string_->append(kPrettyPrintLineEnding); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 133 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 134 | |
| 135 | bool first_value_has_been_output = false; |
| 136 | bool result = true; |
| 137 | for (const auto [key, value] : node) { |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 138 | if (omit_binary_values_ && value.type() == Value::Type::BINARY) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 139 | continue; |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 140 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 141 | |
| 142 | if (first_value_has_been_output) { |
| 143 | json_string_->push_back(','); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 144 | if (pretty_print_) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 145 | json_string_->append(kPrettyPrintLineEnding); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 146 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 147 | } |
| 148 | |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 149 | if (pretty_print_) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 150 | IndentLine(depth + 1U); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 151 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 152 | |
| 153 | EscapeJSONString(key, true, json_string_); |
| 154 | json_string_->push_back(':'); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 155 | if (pretty_print_) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 156 | json_string_->push_back(' '); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 157 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 158 | |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 159 | result &= value.Visit([this, depth = depth + 1](const auto& member) { |
| 160 | return BuildJSONString(member, depth); |
| 161 | }); |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 162 | |
| 163 | first_value_has_been_output = true; |
| 164 | } |
| 165 | |
| 166 | if (pretty_print_) { |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 167 | if (first_value_has_been_output) { |
| Xiaohan Wang | 01d84c36 | 2022-04-15 15:00:08 | [diff] [blame] | 168 | json_string_->append(kPrettyPrintLineEnding); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 169 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 170 | IndentLine(depth); |
| 171 | } |
| 172 | |
| 173 | json_string_->push_back('}'); |
| 174 | return result; |
| 175 | } |
| 176 | |
| 177 | bool JSONWriter::BuildJSONString(const Value::List& node, size_t depth) { |
| 178 | internal::StackMarker depth_check(max_depth_, &stack_depth_); |
| 179 | |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 180 | if (depth_check.IsTooDeep()) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 181 | return false; |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 182 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 183 | |
| 184 | json_string_->push_back('['); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 185 | if (pretty_print_) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 186 | json_string_->push_back(' '); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 187 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 188 | |
| 189 | bool first_value_has_been_output = false; |
| 190 | bool result = true; |
| 191 | for (const auto& value : node) { |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 192 | if (omit_binary_values_ && value.type() == Value::Type::BINARY) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 193 | continue; |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 194 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 195 | |
| 196 | if (first_value_has_been_output) { |
| 197 | json_string_->push_back(','); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 198 | if (pretty_print_) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 199 | json_string_->push_back(' '); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 200 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 201 | } |
| 202 | |
| Daniel Cheng | 8ac305b | 2022-02-17 00:05:11 | [diff] [blame] | 203 | result &= value.Visit([this, depth](const auto& member) { |
| 204 | return BuildJSONString(member, depth); |
| 205 | }); |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 206 | |
| 207 | first_value_has_been_output = true; |
| 208 | } |
| 209 | |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 210 | if (pretty_print_) { |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 211 | json_string_->push_back(' '); |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 212 | } |
| Daniel Cheng | a367fe5 | 2022-02-15 18:08:48 | [diff] [blame] | 213 | json_string_->push_back(']'); |
| 214 | return result; |
| 215 | } |
| 216 | |
| [email protected] | 44ea531 | 2014-01-29 22:50:28 | [diff] [blame] | 217 | void JSONWriter::IndentLine(size_t depth) { |
| 218 | json_string_->append(depth * 3U, ' '); |
| initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 219 | } |
| [email protected] | 93d49d7 | 2009-10-23 20:00:20 | [diff] [blame] | 220 | |
| Arthur Sonzogni | e5fff99c | 2024-02-21 15:58:24 | [diff] [blame] | 221 | std::optional<std::string> WriteJson(ValueView node, size_t max_depth) { |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 222 | std::string result; |
| 223 | if (!JSONWriter::Write(node, &result, max_depth)) { |
| Arthur Sonzogni | e5fff99c | 2024-02-21 15:58:24 | [diff] [blame] | 224 | return std::nullopt; |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 225 | } |
| 226 | return result; |
| 227 | } |
| 228 | |
| Arthur Sonzogni | e5fff99c | 2024-02-21 15:58:24 | [diff] [blame] | 229 | std::optional<std::string> WriteJsonWithOptions(ValueView node, |
| 230 | uint32_t options, |
| 231 | size_t max_depth) { |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 232 | std::string result; |
| 233 | if (!JSONWriter::WriteWithOptions(node, static_cast<int>(options), &result, |
| 234 | max_depth)) { |
| Arthur Sonzogni | e5fff99c | 2024-02-21 15:58:24 | [diff] [blame] | 235 | return std::nullopt; |
| Jeroen Dhollander | 7088e153 | 2023-03-28 10:15:36 | [diff] [blame] | 236 | } |
| 237 | return result; |
| 238 | } |
| 239 | |
| [email protected] | 93d49d7 | 2009-10-23 20:00:20 | [diff] [blame] | 240 | } // namespace base |