载入中...
搜索中...
未找到
Json.cpp
浏览该文件的文档.
1#include "common/Json.h"
2
3#include "common/Value.h"
4
5#include <charconv>
6#include <cmath>
7#include <cstdlib>
8#include <limits>
9#include <locale>
10#include <sstream>
11#include <string_view>
12#include <type_traits>
13#include <utility>
14
15namespace eve::json {
16
17// ---------------------------------------------------------------------------
18// Node
19// ---------------------------------------------------------------------------
20
21struct Node {
22 enum class Kind { Null, Bool, Number, String, Object, Array };
23
25 bool boolVal = false;
26 double numberVal = 0.0;
28 bool integerLiteral = false;
30 bool hasInt64 = false;
31 std::int64_t intVal = 0;
32 std::string stringVal;
33 std::vector<std::string> memberNames; // object keys, document order
34 std::vector<Node> memberValues; // object values, parallel to memberNames
35 std::vector<Node> elements; // array
36};
37
38namespace {
39
44class Parser {
45public:
46 explicit Parser(const std::string& text) : s_(text) {}
47
48 bool parse(Node& out, std::string* error) {
49 skipWs();
50 if (!parseValue(out)) {
51 if (error) *error = "invalid JSON near offset " + std::to_string(pos_);
52 return false;
53 }
54 skipWs();
55 if (pos_ != s_.size()) {
56 if (error) *error = "trailing data at offset " + std::to_string(pos_);
57 return false;
58 }
59 return true;
60 }
61
62private:
63 static constexpr size_t kMaxDepth = 256;
64 const std::string& s_;
65 size_t pos_ = 0;
66
67 void skipWs() {
68 while (pos_ < s_.size() && (s_[pos_] == ' ' || s_[pos_] == '\t' || s_[pos_] == '\n' || s_[pos_] == '\r'))
69 ++pos_;
70 }
71
72 bool peek(char c) const { return pos_ < s_.size() && s_[pos_] == c; }
73
74 bool parseValue(Node& out, size_t depth = 0) {
75 if (depth > kMaxDepth) return false;
76 if (pos_ >= s_.size()) return false;
77 switch (s_[pos_]) {
78 case '{': return parseObject(out, depth);
79 case '[': return parseArray(out, depth);
80 case '"':
81 if (!parseString(out.stringVal)) return false;
83 return true;
84 case 't': return parseLiteral("true", out, true);
85 case 'f': return parseLiteral("false", out, false);
86 case 'n': return parseNull(out);
87 default: return parseNumber(out);
88 }
89 }
90
91 bool parseLiteral(const char* lit, Node& out, bool value) {
92 const size_t n = std::char_traits<char>::length(lit);
93 if (s_.compare(pos_, n, lit) != 0) return false;
94 pos_ += n;
96 out.boolVal = value;
97 return true;
98 }
99
100 bool parseNull(Node& out) {
101 if (s_.compare(pos_, 4, "null") != 0) return false;
102 pos_ += 4;
104 return true;
105 }
106
107 bool parseNumber(Node& out) {
108 const size_t start = pos_;
109 if (pos_ < s_.size() && s_[pos_] == '-') ++pos_;
110 if (pos_ >= s_.size()) return false;
111 if (s_[pos_] == '0') {
112 ++pos_;
113 // RFC 8259 forbids leading zeroes in the integer component.
114 if (pos_ < s_.size() && s_[pos_] >= '0' && s_[pos_] <= '9') return false;
115 } else if (s_[pos_] >= '1' && s_[pos_] <= '9') {
116 while (pos_ < s_.size() && s_[pos_] >= '0' && s_[pos_] <= '9') ++pos_;
117 } else {
118 return false;
119 }
120 bool integral = true;
121 if (pos_ < s_.size() && s_[pos_] == '.') {
122 integral = false;
123 ++pos_;
124 const size_t fractionStart = pos_;
125 while (pos_ < s_.size() && s_[pos_] >= '0' && s_[pos_] <= '9') ++pos_;
126 if (pos_ == fractionStart) return false;
127 }
128 if (pos_ < s_.size() && (s_[pos_] == 'e' || s_[pos_] == 'E')) {
129 integral = false;
130 ++pos_;
131 if (pos_ < s_.size() && (s_[pos_] == '-' || s_[pos_] == '+')) ++pos_;
132 const size_t exponentStart = pos_;
133 while (pos_ < s_.size() && s_[pos_] >= '0' && s_[pos_] <= '9') ++pos_;
134 if (pos_ == exponentStart) return false;
135 }
136 const std::string num = s_.substr(start, pos_ - start);
137 try {
138 size_t used = 0;
139 out.numberVal = std::stod(num, &used);
140 if (used != num.size() || !std::isfinite(out.numberVal)) return false;
141 out.integerLiteral = integral;
142 if (integral) {
143 try {
144 out.intVal = std::stoll(num);
145 out.hasInt64 = true;
146 } catch (...) {
147 // Keep the finite double for compatibility with the
148 // lenient read-only JSON facade. Owning Value conversion
149 // rejects this integer because it cannot preserve Int64.
150 out.hasInt64 = false;
151 }
152 }
153 } catch (...) {
154 // Out of long long range but still a valid double (or vice versa):
155 // keep whichever conversion succeeded.
156 if (!integral) return false;
157 try {
158 out.numberVal = std::stod(num);
159 } catch (...) {
160 return false;
161 }
162 }
164 return true;
165 }
166
167 bool parseString(std::string& out) {
168 if (!peek('"')) return false;
169 ++pos_;
170 out.clear();
171 while (pos_ < s_.size()) {
172 const char c = s_[pos_++];
173 if (c == '"') return true;
174 if (c != '\\') {
175 if (static_cast<unsigned char>(c) < 0x20) return false;
176 out += c;
177 continue;
178 }
179 if (pos_ >= s_.size()) return false;
180 const char esc = s_[pos_++];
181 switch (esc) {
182 case '"': out += '"'; break;
183 case '\\': out += '\\'; break;
184 case '/': out += '/'; break;
185 case 'b': out += '\b'; break;
186 case 'f': out += '\f'; break;
187 case 'n': out += '\n'; break;
188 case 'r': out += '\r'; break;
189 case 't': out += '\t'; break;
190 case 'u': {
191 if (pos_ + 4 > s_.size()) return false;
192 const char hex[5] = {s_[pos_], s_[pos_ + 1], s_[pos_ + 2], s_[pos_ + 3], '\0'};
193 pos_ += 4;
194 char* end = nullptr;
195 const unsigned cp = static_cast<unsigned>(std::strtoul(hex, &end, 16));
196 if (!end || *end != '\0') return false;
197 // A high surrogate followed by "\uXXXX" forms one code point.
198 if (cp >= 0xD800 && cp <= 0xDBFF && pos_ + 6 <= s_.size() && s_[pos_] == '\\' &&
199 s_[pos_ + 1] == 'u') {
200 const char lohex[5] = {s_[pos_ + 2], s_[pos_ + 3], s_[pos_ + 4], s_[pos_ + 5], '\0'};
201 pos_ += 6;
202 char* loEnd = nullptr;
203 const unsigned lo = static_cast<unsigned>(std::strtoul(lohex, &loEnd, 16));
204 if (loEnd && *loEnd == '\0' && lo >= 0xDC00 && lo <= 0xDFFF) {
205 appendUtf8(out, 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00));
206 } else {
207 return false;
208 }
209 } else if (cp >= 0xD800 && cp <= 0xDFFF) {
210 return false;
211 } else {
212 appendUtf8(out, cp);
213 }
214 break;
215 }
216 default: return false;
217 }
218 }
219 return false; // unterminated
220 }
221
222 static void appendUtf8(std::string& out, unsigned cp) {
223 if (cp < 0x80) {
224 out += static_cast<char>(cp);
225 } else if (cp < 0x800) {
226 out += static_cast<char>(0xC0 | (cp >> 6));
227 out += static_cast<char>(0x80 | (cp & 0x3F));
228 } else if (cp < 0x10000) {
229 out += static_cast<char>(0xE0 | (cp >> 12));
230 out += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
231 out += static_cast<char>(0x80 | (cp & 0x3F));
232 } else {
233 out += static_cast<char>(0xF0 | (cp >> 18));
234 out += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
235 out += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
236 out += static_cast<char>(0x80 | (cp & 0x3F));
237 }
238 }
239
240 bool parseObject(Node& out, size_t depth) {
241 ++pos_; // '{'
242 skipWs();
243 if (peek('}')) {
244 ++pos_;
246 return true;
247 }
248 while (true) {
249 skipWs();
250 std::string key;
251 if (!parseString(key)) return false;
252 for (const std::string& existing : out.memberNames)
253 if (existing == key) return false;
254 skipWs();
255 if (!peek(':')) return false;
256 ++pos_;
257 skipWs();
258 Node val;
259 if (!parseValue(val, depth + 1)) return false;
260 out.memberNames.emplace_back(std::move(key));
261 out.memberValues.emplace_back(std::move(val));
262 skipWs();
263 if (peek('}')) {
264 ++pos_;
266 return true;
267 }
268 if (!peek(',')) return false;
269 ++pos_;
270 }
271 }
272
273 bool parseArray(Node& out, size_t depth) {
274 ++pos_; // '['
275 skipWs();
276 if (peek(']')) {
277 ++pos_;
279 return true;
280 }
281 while (true) {
282 skipWs();
283 Node val;
284 if (!parseValue(val, depth + 1)) return false;
285 out.elements.push_back(std::move(val));
286 skipWs();
287 if (peek(']')) {
288 ++pos_;
290 return true;
291 }
292 if (!peek(',')) return false;
293 ++pos_;
294 }
295 }
296};
297
299std::string numberToString(const Node& n) {
300 if (n.hasInt64) return std::to_string(n.intVal);
301 std::ostringstream os;
302 os << n.numberVal;
303 return os.str();
304}
305
306bool stringToDouble(const std::string& s, double& out) {
307 try {
308 size_t used = 0;
309 const double v = std::stod(s, &used);
310 while (used < s.size() && (s[used] == ' ' || s[used] == '\t')) ++used;
311 if (used != s.size()) return false;
312 out = v;
313 return true;
314 } catch (...) {
315 return false;
316 }
317}
318
319} // namespace
320
321// ---------------------------------------------------------------------------
322// Value
323// ---------------------------------------------------------------------------
324
325bool Value::isNull() const { return !node_ || node_->kind == Node::Kind::Null; }
326bool Value::isBool() const { return node_ && node_->kind == Node::Kind::Bool; }
327bool Value::isNumber() const { return node_ && node_->kind == Node::Kind::Number; }
328bool Value::isInt64() const { return node_ && node_->kind == Node::Kind::Number && node_->hasInt64; }
329bool Value::isIntegerLiteral() const { return node_ && node_->kind == Node::Kind::Number && node_->integerLiteral; }
330bool Value::isString() const { return node_ && node_->kind == Node::Kind::String; }
331bool Value::isObject() const { return node_ && node_->kind == Node::Kind::Object; }
332bool Value::isArray() const { return node_ && node_->kind == Node::Kind::Array; }
333
334bool Value::has(const char* key) const { return static_cast<bool>(get(key)); }
335
336Value Value::get(const char* key) const {
337 if (!node_ || node_->kind != Node::Kind::Object || !key) return Value();
338 for (size_t i = 0; i < node_->memberNames.size(); ++i)
339 if (node_->memberNames[i] == key) return Value(&node_->memberValues[i]);
340 return Value();
341}
342
343std::vector<std::string> Value::keys() const {
344 std::vector<std::string> out;
345 if (!node_ || node_->kind != Node::Kind::Object) return out;
346 out.reserve(node_->memberNames.size());
347 for (const auto& name : node_->memberNames) out.push_back(name);
348 return out;
349}
350
351size_t Value::size() const {
352 if (!node_) return 0;
353 if (node_->kind == Node::Kind::Array) return node_->elements.size();
354 if (node_->kind == Node::Kind::Object) return node_->memberValues.size();
355 return 0;
356}
357
358Value Value::at(size_t index) const {
359 if (!node_ || node_->kind != Node::Kind::Array || index >= node_->elements.size())
360 return Value();
361 return Value(&node_->elements[index]);
362}
363
364bool Value::asBool(bool fallback) const {
365 if (!node_) return fallback;
366 switch (node_->kind) {
367 case Node::Kind::Bool: return node_->boolVal;
368 case Node::Kind::Number: return node_->numberVal != 0.0;
370 if (node_->stringVal == "true") return true;
371 if (node_->stringVal == "false") return false;
372 return fallback;
373 default: return fallback;
374 }
375}
376
377double Value::asDouble(double fallback) const {
378 if (!node_) return fallback;
379 switch (node_->kind) {
380 case Node::Kind::Number: return node_->numberVal;
381 case Node::Kind::Bool: return node_->boolVal ? 1.0 : 0.0;
382 case Node::Kind::String: {
383 double v = 0.0;
384 return stringToDouble(node_->stringVal, v) ? v : fallback;
385 }
386 default: return fallback;
387 }
388}
389
390std::int64_t Value::asInt64(std::int64_t fallback) const { return isInt64() ? node_->intVal : fallback; }
391
392int Value::asInt(int fallback) const {
393 if (!node_) return fallback;
394 if (node_->kind == Node::Kind::Number && node_->hasInt64) {
395 if (node_->intVal < std::numeric_limits<int>::min() ||
396 node_->intVal > std::numeric_limits<int>::max())
397 return fallback;
398 return static_cast<int>(node_->intVal);
399 }
400 const double d = asDouble(static_cast<double>(fallback));
401 if (!std::isfinite(d) || d < static_cast<double>(std::numeric_limits<int>::min()) ||
402 d > static_cast<double>(std::numeric_limits<int>::max()))
403 return fallback;
404 return static_cast<int>(d);
405}
406
407float Value::asFloat(float fallback) const {
408 return static_cast<float>(asDouble(static_cast<double>(fallback)));
409}
410
411std::string Value::asString(const std::string& fallback) const {
412 if (!node_) return fallback;
413 switch (node_->kind) {
414 case Node::Kind::String: return node_->stringVal;
415 case Node::Kind::Number: return numberToString(*node_);
416 case Node::Kind::Bool: return node_->boolVal ? "true" : "false";
417 default: return fallback;
418 }
419}
420
421bool Value::getBool(const char* key, bool fallback) const { return get(key).asBool(fallback); }
422int Value::getInt(const char* key, int fallback) const { return get(key).asInt(fallback); }
423float Value::getFloat(const char* key, float fallback) const { return get(key).asFloat(fallback); }
424double Value::getDouble(const char* key, double fallback) const {
425 return get(key).asDouble(fallback);
426}
427std::string Value::getString(const char* key, const std::string& fallback) const {
428 return get(key).asString(fallback);
429}
430
431std::vector<std::string> Value::toStringArray() const {
432 std::vector<std::string> out;
433 if (!node_ || node_->kind != Node::Kind::Array) return out;
434 out.reserve(node_->elements.size());
435 for (const auto& e : node_->elements) out.push_back(Value(&e).asString());
436 return out;
437}
438
439std::vector<std::string> Value::getStringArray(const char* key) const {
440 return get(key).toStringArray();
441}
442
443std::vector<int> Value::getIntArray(const char* key) const {
444 std::vector<int> out;
445 const Value arr = get(key);
446 const size_t n = arr.isArray() ? arr.size() : 0;
447 out.reserve(n);
448 for (size_t i = 0; i < n; ++i) out.push_back(arr.at(i).asInt(0));
449 return out;
450}
451
452std::vector<float> Value::getFloatArray(const char* key) const {
453 std::vector<float> out;
454 const Value arr = get(key);
455 const size_t n = arr.isArray() ? arr.size() : 0;
456 out.reserve(n);
457 for (size_t i = 0; i < n; ++i) out.push_back(arr.at(i).asFloat(0.f));
458 return out;
459}
460
461std::unordered_map<std::string, std::string> Value::getStringMap(const char* key) const {
462 std::unordered_map<std::string, std::string> out;
463 const Value obj = get(key);
464 if (!obj.isObject()) return out;
465 for (const auto& name : obj.keys()) out[name] = obj.getString(name.c_str());
466 return out;
467}
468
469std::unordered_map<std::string, int> Value::getIntMap(const char* key) const {
470 std::unordered_map<std::string, int> out;
471 const Value obj = get(key);
472 if (!obj.isObject()) return out;
473 for (const auto& name : obj.keys()) out[name] = obj.getInt(name.c_str(), 0);
474 return out;
475}
476
477namespace {
478
479void appendEscapedString(std::string_view value, std::string& output) {
480 constexpr char hex[] = "0123456789abcdef";
481 output.push_back('"');
482 for (const unsigned char byte : value) {
483 switch (byte) {
484 case '"': output += "\\\""; break;
485 case '\\': output += "\\\\"; break;
486 case '\b': output += "\\b"; break;
487 case '\f': output += "\\f"; break;
488 case '\n': output += "\\n"; break;
489 case '\r': output += "\\r"; break;
490 case '\t': output += "\\t"; break;
491 default:
492 if (byte < 0x20) {
493 output += "\\u00";
494 output.push_back(hex[byte >> 4]);
495 output.push_back(hex[byte & 0x0f]);
496 } else {
497 output.push_back(static_cast<char>(byte));
498 }
499 break;
500 }
501 }
502 output.push_back('"');
503}
504
505bool appendCanonicalJson(const eve::Value& value, std::string& output) {
506 return std::visit(
507 [&output](const auto& current) -> bool {
508 using T = std::decay_t<decltype(current)>;
509 if constexpr (std::is_same_v<T, std::monostate>) {
510 output += "null";
511 return true;
512 } else if constexpr (std::is_same_v<T, bool>) {
513 output += current ? "true" : "false";
514 return true;
515 } else if constexpr (std::is_same_v<T, std::int64_t>) {
516 char buffer[32];
517 const auto [end, error] = std::to_chars(buffer, buffer + sizeof(buffer), current);
518 if (error != std::errc()) return false;
519 output.append(buffer, end);
520 return true;
521 } else if constexpr (std::is_same_v<T, double>) {
522 if (!std::isfinite(current)) return false;
523 std::ostringstream stream;
524 stream.imbue(std::locale::classic());
525 stream.precision(std::numeric_limits<double>::max_digits10);
526 stream << current;
527 const std::string number = stream.str();
528 output += number;
529 if (number.find_first_of(".eE") == std::string_view::npos) output += ".0";
530 return true;
531 } else if constexpr (std::is_same_v<T, std::string>) {
532 appendEscapedString(current, output);
533 return true;
534 } else if constexpr (std::is_same_v<T, eve::Value::Array>) {
535 output.push_back('[');
536 bool first = true;
537 for (const eve::Value& element : current) {
538 if (!first) output.push_back(',');
539 first = false;
540 if (!appendCanonicalJson(element, output)) return false;
541 }
542 output.push_back(']');
543 return true;
544 } else if constexpr (std::is_same_v<T, eve::Value::Object>) {
545 output.push_back('{');
546 bool first = true;
547 for (const auto& [key, element] : current) {
548 if (!first) output.push_back(',');
549 first = false;
550 appendEscapedString(key, output);
551 output.push_back(':');
552 if (!appendCanonicalJson(element, output)) return false;
553 }
554 output.push_back('}');
555 return true;
556 }
557 },
558 value.storage());
559}
560
561} // namespace
562
564 std::string output;
565 if (!appendCanonicalJson(value, output)) {
567 DiagnosticCode::SerializationError, "Value contains a non-finite Double; JSON requires finite numbers"));
568 }
569 return Result<std::string>::success(std::move(output));
570}
571
572// ---------------------------------------------------------------------------
573// Document
574// ---------------------------------------------------------------------------
575
576Document::Document() = default;
577Document::~Document() = default;
578Document::Document(Document&&) noexcept = default;
579Document& Document::operator=(Document&&) noexcept = default;
580
581Document Document::parse(const std::string& text, std::string* error) {
582 Document doc;
583 auto node = std::make_unique<Node>();
584 Parser parser(text);
585 if (!parser.parse(*node, error)) return doc;
586 doc.root_ = std::move(node);
587 return doc;
588}
589
590} // namespace eve::json
double value
Duration start
std::string output
const std::string & s
std::uint32_t key
glm::vec3 n
Definition Grass.cpp:63
float v
std::int32_t c
std::int32_t first
std::string text
std::string name
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
std::string error
Definition Package.cpp:60
float d
const RoadNode * node
double number
double current
std::string element
uint32_t index
std::uint32_t depth
Owning, renderer-independent dynamic values.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
const std::string & asString() const
Return the string payload; the caller must have checked the kind.
Definition Value.cpp:87
double asDouble() const
Return the Double payload; the caller must have checked the kind.
Definition Value.cpp:75
std::int64_t asInt() const
Return the Int64 payload; the caller must have checked the kind.
Definition Value.cpp:69
bool asBool() const
Return the boolean payload; the caller must have checked the kind.
Definition Value.cpp:81
EVENGINE_API_FOUNDATION public API.
Definition Json.h:136
~Document()
Document.
EVENGINE_API_FOUNDATION public API.
Definition Json.h:38
float asFloat(float fallback=0.f) const
As float.
Definition Json.cpp:407
bool getBool(const char *key, bool fallback=false) const
Returns the bool.
Definition Json.cpp:421
bool isObject() const
True when object.
Definition Json.cpp:331
bool has(const char *key) const
True when active.
Definition Json.cpp:334
double asDouble(double fallback=0.0) const
As double.
Definition Json.cpp:377
bool isIntegerLiteral() const
True when the JSON number had no fraction or exponent.
Definition Json.cpp:329
Value get(const char *key) const
Returns the value.
Definition Json.cpp:336
size_t size() const
Returns the size of .
Definition Json.cpp:351
bool isArray() const
True when array.
Definition Json.cpp:332
Value at(size_t index) const
At.
Definition Json.cpp:358
bool isString() const
True when string.
Definition Json.cpp:330
float getFloat(const char *key, float fallback=0.f) const
Returns the float.
Definition Json.cpp:423
int getInt(const char *key, int fallback=0) const
Returns the int.
Definition Json.cpp:422
Value()=default
Value.
bool isNull() const
True when null.
Definition Json.cpp:325
bool isNumber() const
True when number.
Definition Json.cpp:327
std::string getString(const char *key, const std::string &fallback={}) const
Returns the string.
Definition Json.cpp:427
int asInt(int fallback=0) const
As int.
Definition Json.cpp:392
std::vector< std::string > getStringArray(const char *key) const
Returns the string array.
Definition Json.cpp:439
bool asBool(bool fallback=false) const
As bool.
Definition Json.cpp:364
double getDouble(const char *key, double fallback=0.0) const
Returns the double.
Definition Json.cpp:424
bool isBool() const
True when bool.
Definition Json.cpp:326
std::vector< std::string > keys() const
Keys.
Definition Json.cpp:343
std::string asString(const std::string &fallback={}) const
As string.
Definition Json.cpp:411
bool isInt64() const
True when the JSON number was an integer literal that fits Int64.
Definition Json.cpp:328
std::unordered_map< std::string, int > getIntMap(const char *key) const
Definition Json.cpp:469
std::int64_t asInt64(std::int64_t fallback=0) const
Read an exact Int64 literal, or return the caller-supplied default for other numbers.
Definition Json.cpp:390
std::unordered_map< std::string, std::string > getStringMap(const char *key) const
Definition Json.cpp:461
std::vector< int > getIntArray(const char *key) const
Returns the int array.
Definition Json.cpp:443
std::vector< std::string > toStringArray() const
To string array.
Definition Json.cpp:431
std::vector< float > getFloatArray(const char *key) const
Returns the float array.
Definition Json.cpp:452
Result< std::string > stringify(const eve::Value &value)
Serialize the canonical owning Value as deterministic compact JSON.
Definition Json.cpp:563
std::int64_t intVal
Definition Json.cpp:31
std::string stringVal
Definition Json.cpp:32
std::vector< Node > elements
Definition Json.cpp:35
double numberVal
Definition Json.cpp:26
std::vector< std::string > memberNames
Definition Json.cpp:33
bool hasInt64
Definition Json.cpp:30
std::vector< Node > memberValues
Definition Json.cpp:34
bool integerLiteral
Definition Json.cpp:28