Merge pull request #669 from simdjson/jkeiser/type-switch
Add element.type() for type switching
This commit is contained in:
commit
c5e21a2469
|
@ -8,8 +8,9 @@ An overview of what you need to know to use simdjson, with examples.
|
|||
* [Using the Parsed JSON](#using-the-parsed-json)
|
||||
* [JSON Pointer](#json-pointer)
|
||||
* [Error Handling](#error-handling)
|
||||
* [Error Handling Example](#error-handling-example)
|
||||
* [Exceptions](#exceptions)
|
||||
* [Error Handling Example](#error-handling-example)
|
||||
* [Exceptions](#exceptions)
|
||||
* [Tree Walking and JSON Element Types](#tree-walking-and-json-element-types)
|
||||
* [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
|
||||
* [Thread Safety](#thread-safety)
|
||||
|
||||
|
@ -67,6 +68,8 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
|
|||
first element.
|
||||
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
|
||||
> O(1) operation, which it is not presently in simdjson.
|
||||
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
|
||||
returns an `element_type`.
|
||||
|
||||
Here are some examples of all of the above:
|
||||
|
||||
|
@ -152,9 +155,9 @@ This is how the example in "Using the Parsed JSON" could be written using only e
|
|||
|
||||
```c++
|
||||
auto cars_json = R"( [
|
||||
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
|
||||
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
|
||||
] )"_padded;
|
||||
dom::parser parser;
|
||||
auto [cars, error] = parser.parse(cars_json).get<dom::array>();
|
||||
|
@ -210,6 +213,60 @@ dom::element doc = parser.parse(json); // Throws an exception if there was an er
|
|||
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
|
||||
program from continuing if there was an error.
|
||||
|
||||
Tree Walking and JSON Element Types
|
||||
-----------------------------------
|
||||
|
||||
Sometimes you don't necessarily have a document with a known type, and are trying to generically
|
||||
inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For
|
||||
example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON
|
||||
(* ignoring nuances like trailing commas and escaping strings, for brevity's sake):
|
||||
|
||||
```c++
|
||||
void print_json(dom::element element) {
|
||||
switch (element.type()) {
|
||||
case dom::element_type::ARRAY:
|
||||
cout << "[";
|
||||
for (dom::element child : dom::array(element)) {
|
||||
print_json(child);
|
||||
cout << ",";
|
||||
}
|
||||
cout << "]";
|
||||
break;
|
||||
case dom::element_type::OBJECT:
|
||||
cout << "{";
|
||||
for (dom::key_value_pair field : dom::object(element)) {
|
||||
cout << "\"" << field.key << "\": ";
|
||||
print_json(field.value);
|
||||
}
|
||||
cout << "}";
|
||||
break;
|
||||
case dom::element_type::INT64:
|
||||
cout << int64_t(element) << endl;
|
||||
break;
|
||||
case dom::element_type::UINT64:
|
||||
cout << uint64_t(element) << endl;
|
||||
break;
|
||||
case dom::element_type::DOUBLE:
|
||||
cout << double(element) << endl;
|
||||
break;
|
||||
case dom::element_type::STRING:
|
||||
cout << std::string_view(element) << endl;
|
||||
break;
|
||||
case dom::element_type::BOOL:
|
||||
cout << bool(element) << endl;
|
||||
break;
|
||||
case dom::element_type::NULL_VALUE:
|
||||
cout << "null" << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void basics_treewalk_1() {
|
||||
dom::parser parser;
|
||||
print_json(parser.load("twitter.json"));
|
||||
}
|
||||
```
|
||||
|
||||
Newline-Delimited JSON (ndjson) and JSON lines
|
||||
----------------------------------------------
|
||||
|
||||
|
|
|
@ -41,50 +41,65 @@ namespace simdjson::internal {
|
|||
using namespace simdjson::dom;
|
||||
|
||||
constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF;
|
||||
enum class tape_type;
|
||||
class tape_ref;
|
||||
/**
|
||||
* The possible types in the tape. Internal only.
|
||||
*/
|
||||
enum class tape_type {
|
||||
ROOT = 'r',
|
||||
START_ARRAY = '[',
|
||||
START_OBJECT = '{',
|
||||
END_ARRAY = ']',
|
||||
END_OBJECT = '}',
|
||||
STRING = '"',
|
||||
INT64 = 'l',
|
||||
UINT64 = 'u',
|
||||
DOUBLE = 'd',
|
||||
TRUE_VALUE = 't',
|
||||
FALSE_VALUE = 'f',
|
||||
NULL_VALUE = 'n'
|
||||
};
|
||||
|
||||
/**
|
||||
* A reference to an element on the tape. Internal only.
|
||||
*/
|
||||
class tape_ref {
|
||||
public:
|
||||
really_inline tape_ref() noexcept;
|
||||
really_inline tape_ref(const document *doc, size_t json_index) noexcept;
|
||||
inline size_t after_element() const noexcept;
|
||||
really_inline tape_type type() const noexcept;
|
||||
really_inline uint64_t tape_value() const noexcept;
|
||||
template<typename T>
|
||||
really_inline T next_tape_value() const noexcept;
|
||||
inline std::string_view get_string_view() const noexcept;
|
||||
/**
|
||||
* The possible types in the tape. Internal only.
|
||||
*/
|
||||
enum class tape_type {
|
||||
ROOT = 'r',
|
||||
START_ARRAY = '[',
|
||||
START_OBJECT = '{',
|
||||
END_ARRAY = ']',
|
||||
END_OBJECT = '}',
|
||||
STRING = '"',
|
||||
INT64 = 'l',
|
||||
UINT64 = 'u',
|
||||
DOUBLE = 'd',
|
||||
TRUE_VALUE = 't',
|
||||
FALSE_VALUE = 'f',
|
||||
NULL_VALUE = 'n'
|
||||
};
|
||||
|
||||
/** The document this element references. */
|
||||
const document *doc;
|
||||
/**
|
||||
* A reference to an element on the tape. Internal only.
|
||||
*/
|
||||
class tape_ref {
|
||||
public:
|
||||
really_inline tape_ref() noexcept;
|
||||
really_inline tape_ref(const document *doc, size_t json_index) noexcept;
|
||||
inline size_t after_element() const noexcept;
|
||||
really_inline tape_type tape_ref_type() const noexcept;
|
||||
really_inline uint64_t tape_value() const noexcept;
|
||||
template<typename T>
|
||||
really_inline T next_tape_value() const noexcept;
|
||||
inline std::string_view get_string_view() const noexcept;
|
||||
|
||||
/** The document this element references. */
|
||||
const document *doc;
|
||||
|
||||
/** The index of this element on `doc.tape[]` */
|
||||
size_t json_index;
|
||||
};
|
||||
|
||||
/** The index of this element on `doc.tape[]` */
|
||||
size_t json_index;
|
||||
};
|
||||
} // namespace simdjson::internal
|
||||
|
||||
namespace simdjson::dom {
|
||||
|
||||
/**
|
||||
* The actual concrete type of a JSON element
|
||||
* This is the type it is most easily cast to with get<>.
|
||||
*/
|
||||
enum class element_type {
|
||||
ARRAY, ///< dom::array
|
||||
OBJECT, ///< dom::object
|
||||
INT64, ///< int64_t
|
||||
UINT64, ///< uint64_t: any integer that fits in uint64_t but *not* int64_t
|
||||
DOUBLE, ///< double: Any number with a "." or "e" that fits in double.
|
||||
STRING, ///< std::string_view
|
||||
BOOL, ///< bool
|
||||
NULL_VALUE ///< null
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON array.
|
||||
*/
|
||||
|
@ -367,6 +382,9 @@ public:
|
|||
/** Create a new, invalid element. */
|
||||
really_inline element() noexcept;
|
||||
|
||||
/** The type of this element. */
|
||||
really_inline element_type type() const noexcept;
|
||||
|
||||
/** Whether this element is a json `null`. */
|
||||
really_inline bool is_null() const noexcept;
|
||||
|
||||
|
@ -1121,6 +1139,36 @@ inline std::ostream& operator<<(std::ostream& out, const object &value) { return
|
|||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, const key_value_pair &value) { return out << minify(value); }
|
||||
|
||||
/**
|
||||
* Print element type to an output stream.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The value to print.
|
||||
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, element_type type) {
|
||||
switch (type) {
|
||||
case element_type::ARRAY:
|
||||
return out << "array";
|
||||
case element_type::OBJECT:
|
||||
return out << "object";
|
||||
case element_type::INT64:
|
||||
return out << "int64_t";
|
||||
case element_type::UINT64:
|
||||
return out << "uint64_t";
|
||||
case element_type::DOUBLE:
|
||||
return out << "double";
|
||||
case element_type::STRING:
|
||||
return out << "string";
|
||||
case element_type::BOOL:
|
||||
return out << "bool";
|
||||
case element_type::NULL_VALUE:
|
||||
return out << "null";
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
@ -1172,6 +1220,7 @@ public:
|
|||
really_inline simdjson_result(dom::element &&value) noexcept; ///< @private
|
||||
really_inline simdjson_result(error_code error) noexcept; ///< @private
|
||||
|
||||
inline simdjson_result<dom::element_type> type() const noexcept;
|
||||
inline simdjson_result<bool> is_null() const noexcept;
|
||||
template<typename T>
|
||||
inline simdjson_result<bool> is() const noexcept;
|
||||
|
|
|
@ -23,6 +23,10 @@ really_inline simdjson_result<dom::element>::simdjson_result(dom::element &&valu
|
|||
: internal::simdjson_result_base<dom::element>(std::forward<dom::element>(value)) {}
|
||||
really_inline simdjson_result<dom::element>::simdjson_result(error_code error) noexcept
|
||||
: internal::simdjson_result_base<dom::element>(error) {}
|
||||
inline simdjson_result<dom::element_type> simdjson_result<dom::element>::type() const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.type();
|
||||
}
|
||||
inline simdjson_result<bool> simdjson_result<dom::element>::is_null() const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.is_null();
|
||||
|
@ -715,13 +719,39 @@ inline key_value_pair::key_value_pair(const std::string_view &_key, element _val
|
|||
really_inline element::element() noexcept : internal::tape_ref() {}
|
||||
really_inline element::element(const document *_doc, size_t _json_index) noexcept : internal::tape_ref(_doc, _json_index) { }
|
||||
|
||||
inline element_type element::type() const noexcept {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::START_ARRAY:
|
||||
return element_type::ARRAY;
|
||||
case internal::tape_type::START_OBJECT:
|
||||
return element_type::OBJECT;
|
||||
case internal::tape_type::INT64:
|
||||
return element_type::INT64;
|
||||
case internal::tape_type::UINT64:
|
||||
return element_type::UINT64;
|
||||
case internal::tape_type::DOUBLE:
|
||||
return element_type::DOUBLE;
|
||||
case internal::tape_type::STRING:
|
||||
return element_type::STRING;
|
||||
case internal::tape_type::TRUE_VALUE:
|
||||
case internal::tape_type::FALSE_VALUE:
|
||||
return element_type::BOOL;
|
||||
case internal::tape_type::NULL_VALUE:
|
||||
return element_type::NULL_VALUE;
|
||||
case internal::tape_type::ROOT:
|
||||
case internal::tape_type::END_ARRAY:
|
||||
case internal::tape_type::END_OBJECT:
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
really_inline bool element::is_null() const noexcept {
|
||||
return type() == internal::tape_type::NULL_VALUE;
|
||||
return tape_ref_type() == internal::tape_type::NULL_VALUE;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simdjson_result<bool> element::get<bool>() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::TRUE_VALUE:
|
||||
return true;
|
||||
case internal::tape_type::FALSE_VALUE:
|
||||
|
@ -732,7 +762,7 @@ inline simdjson_result<bool> element::get<bool>() const noexcept {
|
|||
}
|
||||
template<>
|
||||
inline simdjson_result<const char *> element::get<const char *>() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::STRING: {
|
||||
size_t string_buf_index = tape_value();
|
||||
return reinterpret_cast<const char *>(&doc->string_buf[string_buf_index + sizeof(uint32_t)]);
|
||||
|
@ -743,7 +773,7 @@ inline simdjson_result<const char *> element::get<const char *>() const noexcept
|
|||
}
|
||||
template<>
|
||||
inline simdjson_result<std::string_view> element::get<std::string_view>() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::STRING:
|
||||
return get_string_view();
|
||||
default:
|
||||
|
@ -752,7 +782,7 @@ inline simdjson_result<std::string_view> element::get<std::string_view>() const
|
|||
}
|
||||
template<>
|
||||
inline simdjson_result<uint64_t> element::get<uint64_t>() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::UINT64:
|
||||
return next_tape_value<uint64_t>();
|
||||
case internal::tape_type::INT64: {
|
||||
|
@ -768,11 +798,11 @@ inline simdjson_result<uint64_t> element::get<uint64_t>() const noexcept {
|
|||
}
|
||||
template<>
|
||||
inline simdjson_result<int64_t> element::get<int64_t>() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::UINT64: {
|
||||
uint64_t result = next_tape_value<uint64_t>();
|
||||
// Wrapping max in parens to handle Windows issue: https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std
|
||||
if (result > (std::numeric_limits<uint64_t>::max)()) {
|
||||
if (result > (std::numeric_limits<int64_t>::max)()) {
|
||||
return NUMBER_OUT_OF_RANGE;
|
||||
}
|
||||
return static_cast<int64_t>(result);
|
||||
|
@ -785,7 +815,7 @@ inline simdjson_result<int64_t> element::get<int64_t>() const noexcept {
|
|||
}
|
||||
template<>
|
||||
inline simdjson_result<double> element::get<double>() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::UINT64:
|
||||
return next_tape_value<uint64_t>();
|
||||
case internal::tape_type::INT64: {
|
||||
|
@ -804,7 +834,7 @@ inline simdjson_result<double> element::get<double>() const noexcept {
|
|||
}
|
||||
template<>
|
||||
inline simdjson_result<array> element::get<array>() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::START_ARRAY:
|
||||
return array(doc, json_index);
|
||||
default:
|
||||
|
@ -813,7 +843,7 @@ inline simdjson_result<array> element::get<array>() const noexcept {
|
|||
}
|
||||
template<>
|
||||
inline simdjson_result<object> element::get<object>() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::START_OBJECT:
|
||||
return object(doc, json_index);
|
||||
default:
|
||||
|
@ -838,10 +868,10 @@ inline element::operator double() const noexcept(false) { return get<double>();
|
|||
inline element::operator array() const noexcept(false) { return get<array>(); }
|
||||
inline element::operator object() const noexcept(false) { return get<object>(); }
|
||||
|
||||
inline dom::array::iterator dom::element::begin() const noexcept(false) {
|
||||
inline array::iterator element::begin() const noexcept(false) {
|
||||
return get<array>().begin();
|
||||
}
|
||||
inline dom::array::iterator dom::element::end() const noexcept(false) {
|
||||
inline array::iterator element::end() const noexcept(false) {
|
||||
return get<array>().end();
|
||||
}
|
||||
|
||||
|
@ -854,7 +884,7 @@ inline simdjson_result<element> element::operator[](const char *key) const noexc
|
|||
return at_key(key);
|
||||
}
|
||||
inline simdjson_result<element> element::at(const std::string_view &json_pointer) const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case internal::tape_type::START_OBJECT:
|
||||
return object(doc, json_index).at(json_pointer);
|
||||
case internal::tape_type::START_ARRAY:
|
||||
|
@ -905,7 +935,7 @@ inline std::ostream& minify<dom::element>::print(std::ostream& out) {
|
|||
out << '"' << internal::escape_json_string(iter.get_string_view()) << "\":";
|
||||
iter.json_index++;
|
||||
}
|
||||
switch (iter.type()) {
|
||||
switch (iter.tape_ref_type()) {
|
||||
|
||||
// Arrays
|
||||
case tape_type::START_ARRAY: {
|
||||
|
@ -923,7 +953,7 @@ inline std::ostream& minify<dom::element>::print(std::ostream& out) {
|
|||
iter.json_index++;
|
||||
|
||||
// Handle empty [] (we don't want to come back around and print commas)
|
||||
if (iter.type() == tape_type::END_ARRAY) {
|
||||
if (iter.tape_ref_type() == tape_type::END_ARRAY) {
|
||||
out << ']';
|
||||
depth--;
|
||||
break;
|
||||
|
@ -950,7 +980,7 @@ inline std::ostream& minify<dom::element>::print(std::ostream& out) {
|
|||
iter.json_index++;
|
||||
|
||||
// Handle empty {} (we don't want to come back around and print commas)
|
||||
if (iter.type() == tape_type::END_OBJECT) {
|
||||
if (iter.tape_ref_type() == tape_type::END_OBJECT) {
|
||||
out << '}';
|
||||
depth--;
|
||||
break;
|
||||
|
@ -997,8 +1027,8 @@ inline std::ostream& minify<dom::element>::print(std::ostream& out) {
|
|||
after_value = true;
|
||||
|
||||
// Handle multiple ends in a row
|
||||
while (depth != 0 && (iter.type() == tape_type::END_ARRAY || iter.type() == tape_type::END_OBJECT)) {
|
||||
out << char(iter.type());
|
||||
while (depth != 0 && (iter.tape_ref_type() == tape_type::END_ARRAY || iter.tape_ref_type() == tape_type::END_OBJECT)) {
|
||||
out << char(iter.tape_ref_type());
|
||||
depth--;
|
||||
iter.json_index++;
|
||||
}
|
||||
|
@ -1070,7 +1100,7 @@ really_inline tape_ref::tape_ref() noexcept : doc{nullptr}, json_index{0} {}
|
|||
really_inline tape_ref::tape_ref(const document *_doc, size_t _json_index) noexcept : doc{_doc}, json_index{_json_index} {}
|
||||
|
||||
inline size_t tape_ref::after_element() const noexcept {
|
||||
switch (type()) {
|
||||
switch (tape_ref_type()) {
|
||||
case tape_type::START_ARRAY:
|
||||
case tape_type::START_OBJECT:
|
||||
return tape_value();
|
||||
|
@ -1082,7 +1112,7 @@ inline size_t tape_ref::after_element() const noexcept {
|
|||
return json_index + 1;
|
||||
}
|
||||
}
|
||||
really_inline tape_type tape_ref::type() const noexcept {
|
||||
really_inline tape_type tape_ref::tape_ref_type() const noexcept {
|
||||
return static_cast<tape_type>(doc->tape[json_index] >> 56);
|
||||
}
|
||||
really_inline uint64_t internal::tape_ref::tape_value() const noexcept {
|
||||
|
|
|
@ -23,6 +23,11 @@
|
|||
#define NDJSON_TEST_PATH "jsonexamples/amazon_cellphones.ndjson"
|
||||
#endif
|
||||
|
||||
#define ASSERT_EQUAL(ACTUAL, EXPECTED) if ((ACTUAL) != (EXPECTED)) { std::cerr << "Expected " << #ACTUAL << " to be " << (EXPECTED) << ", got " << (ACTUAL) << " instead!" << std::endl; return false; }
|
||||
#define ASSERT_TRUE(ACTUAL) ASSERT_EQUAL(ACTUAL, true)
|
||||
#define ASSERT_FALSE(ACTUAL) ASSERT_EQUAL(ACTUAL, false)
|
||||
#define ASSERT_SUCCESS(ERROR) if (ERROR) { std::cerr << (ERROR) << std::endl; return false; }
|
||||
|
||||
namespace number_tests {
|
||||
|
||||
// ulp distance
|
||||
|
@ -1104,6 +1109,488 @@ namespace dom_api_tests {
|
|||
}
|
||||
}
|
||||
|
||||
namespace type_tests {
|
||||
using namespace simdjson;
|
||||
using namespace std;
|
||||
|
||||
const padded_string ALL_TYPES_JSON = R"(
|
||||
{
|
||||
"array": [],
|
||||
|
||||
"object": {},
|
||||
|
||||
"string": "foo",
|
||||
|
||||
"0": 0,
|
||||
"1": 1,
|
||||
"-1": -1,
|
||||
"9223372036854775807": 9223372036854775807,
|
||||
"-9223372036854775808": -9223372036854775808,
|
||||
|
||||
"9223372036854775808": 9223372036854775808,
|
||||
"18446744073709551615": 18446744073709551615,
|
||||
|
||||
"0.0": 0.0,
|
||||
"0.1": 0.1,
|
||||
"1e0": 1e0,
|
||||
"1e100": 1e100,
|
||||
|
||||
"true": true,
|
||||
"false": false,
|
||||
|
||||
"null": null
|
||||
}
|
||||
)"_padded;
|
||||
|
||||
bool test_array() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
|
||||
const auto key = "array";
|
||||
const auto expected_type = dom::element_type::ARRAY;
|
||||
|
||||
dom::parser parser;
|
||||
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
|
||||
ASSERT_SUCCESS(result.error());
|
||||
|
||||
// Test simdjson_result<element>.is<T>() (error chain)
|
||||
ASSERT_TRUE(result.is<dom::array>());
|
||||
ASSERT_FALSE(result.is<dom::object>());
|
||||
ASSERT_FALSE(result.is<std::string_view>());
|
||||
ASSERT_FALSE(result.is<const char *>());
|
||||
ASSERT_FALSE(result.is<int64_t>());
|
||||
ASSERT_FALSE(result.is<uint64_t>());
|
||||
ASSERT_FALSE(result.is<double>());
|
||||
ASSERT_FALSE(result.is<bool>());
|
||||
ASSERT_FALSE(result.is_null());
|
||||
|
||||
// Test simdjson_result<element>.type() (error chain)
|
||||
simdjson::error_code error;
|
||||
dom::element_type type;
|
||||
result.type().tie(type, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(type, expected_type);
|
||||
|
||||
// Test element.is<T>()
|
||||
dom::element element = result.first;
|
||||
ASSERT_TRUE(element.is<dom::array>());
|
||||
ASSERT_FALSE(element.is<dom::object>());
|
||||
ASSERT_FALSE(element.is<std::string_view>());
|
||||
ASSERT_FALSE(element.is<const char *>());
|
||||
ASSERT_FALSE(element.is<int64_t>());
|
||||
ASSERT_FALSE(element.is<uint64_t>());
|
||||
ASSERT_FALSE(element.is<double>());
|
||||
ASSERT_FALSE(element.is<bool>());
|
||||
ASSERT_FALSE(element.is_null());
|
||||
|
||||
// Test element.type()
|
||||
ASSERT_EQUAL(element.type(), expected_type);
|
||||
|
||||
// Test element.get<T>()
|
||||
dom::array value;
|
||||
result.get<dom::array>().tie(value, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_object() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
|
||||
const auto key = "object";
|
||||
const auto expected_type = dom::element_type::OBJECT;
|
||||
|
||||
dom::parser parser;
|
||||
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
|
||||
ASSERT_SUCCESS(result.error());
|
||||
|
||||
// Test simdjson_result<element>.is<T>() (error chain)
|
||||
ASSERT_FALSE(result.is<dom::array>());
|
||||
ASSERT_TRUE(result.is<dom::object>());
|
||||
ASSERT_FALSE(result.is<std::string_view>());
|
||||
ASSERT_FALSE(result.is<const char *>());
|
||||
ASSERT_FALSE(result.is<int64_t>());
|
||||
ASSERT_FALSE(result.is<uint64_t>());
|
||||
ASSERT_FALSE(result.is<double>());
|
||||
ASSERT_FALSE(result.is<bool>());
|
||||
ASSERT_FALSE(result.is_null());
|
||||
|
||||
// Test simdjson_result<element>.type() (error chain)
|
||||
simdjson::error_code error;
|
||||
dom::element_type type;
|
||||
result.type().tie(type, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(type, expected_type);
|
||||
|
||||
// Test element.is<T>()
|
||||
dom::element element = result.first;
|
||||
ASSERT_FALSE(element.is<dom::array>());
|
||||
ASSERT_TRUE(element.is<dom::object>());
|
||||
ASSERT_FALSE(element.is<std::string_view>());
|
||||
ASSERT_FALSE(element.is<const char *>());
|
||||
ASSERT_FALSE(element.is<int64_t>());
|
||||
ASSERT_FALSE(element.is<uint64_t>());
|
||||
ASSERT_FALSE(element.is<double>());
|
||||
ASSERT_FALSE(element.is<bool>());
|
||||
ASSERT_FALSE(element.is_null());
|
||||
|
||||
// Test element.type()
|
||||
ASSERT_EQUAL(element.type(), expected_type);
|
||||
|
||||
// Test element.get<T>()
|
||||
dom::object value;
|
||||
result.get<dom::object>().tie(value, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_string() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
|
||||
const auto key = "string";
|
||||
const auto expected_type = dom::element_type::STRING;
|
||||
|
||||
dom::parser parser;
|
||||
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
|
||||
ASSERT_SUCCESS(result.error());
|
||||
|
||||
// Test simdjson_result<element>.is<T>() (error chain)
|
||||
ASSERT_FALSE(result.is<dom::array>());
|
||||
ASSERT_FALSE(result.is<dom::object>());
|
||||
ASSERT_TRUE(result.is<std::string_view>());
|
||||
ASSERT_TRUE(result.is<const char *>());
|
||||
ASSERT_FALSE(result.is<int64_t>());
|
||||
ASSERT_FALSE(result.is<uint64_t>());
|
||||
ASSERT_FALSE(result.is<double>());
|
||||
ASSERT_FALSE(result.is<bool>());
|
||||
ASSERT_FALSE(result.is_null());
|
||||
|
||||
// Test simdjson_result<element>.type() (error chain)
|
||||
simdjson::error_code error;
|
||||
dom::element_type type;
|
||||
result.type().tie(type, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(type, expected_type);
|
||||
|
||||
// Test element.is<T>()
|
||||
dom::element element = result.first;
|
||||
ASSERT_FALSE(element.is<dom::array>());
|
||||
ASSERT_FALSE(element.is<dom::object>());
|
||||
ASSERT_TRUE(element.is<std::string_view>());
|
||||
ASSERT_TRUE(element.is<const char *>());
|
||||
ASSERT_FALSE(element.is<int64_t>());
|
||||
ASSERT_FALSE(element.is<uint64_t>());
|
||||
ASSERT_FALSE(element.is<double>());
|
||||
ASSERT_FALSE(element.is<bool>());
|
||||
ASSERT_FALSE(element.is_null());
|
||||
|
||||
// Test element.type()
|
||||
ASSERT_EQUAL(element.type(), expected_type);
|
||||
|
||||
// Test element.get<T>()
|
||||
std::string_view value;
|
||||
result.get<std::string_view>().tie(value, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(value, string_view("foo"));
|
||||
|
||||
const char *value2;
|
||||
result.get<const char *>().tie(value2, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(string_view(value2), string_view("foo"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_int64(const char *key, int64_t expected_value) {
|
||||
std::cout << "Running " << __func__ << "(" << key << ")" << std::endl;
|
||||
|
||||
const auto expected_type = dom::element_type::INT64;
|
||||
|
||||
dom::parser parser;
|
||||
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
|
||||
ASSERT_SUCCESS(result.error());
|
||||
|
||||
// Test simdjson_result<element>.is<T>() (error chain)
|
||||
ASSERT_FALSE(result.is<dom::array>());
|
||||
ASSERT_FALSE(result.is<dom::object>());
|
||||
ASSERT_FALSE(result.is<std::string_view>());
|
||||
ASSERT_FALSE(result.is<const char *>());
|
||||
ASSERT_TRUE(result.is<int64_t>());
|
||||
if (expected_value < 0) {
|
||||
ASSERT_FALSE(result.is<uint64_t>());
|
||||
} else {
|
||||
ASSERT_TRUE(result.is<uint64_t>());
|
||||
}
|
||||
ASSERT_TRUE(result.is<double>());
|
||||
ASSERT_FALSE(result.is<bool>());
|
||||
ASSERT_FALSE(result.is_null());
|
||||
|
||||
// Test simdjson_result<element>.type() (error chain)
|
||||
simdjson::error_code error;
|
||||
dom::element_type type;
|
||||
result.type().tie(type, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(type, expected_type);
|
||||
|
||||
// Test element.is<T>()
|
||||
dom::element element = result.first;
|
||||
ASSERT_FALSE(element.is<dom::array>());
|
||||
ASSERT_FALSE(element.is<dom::object>());
|
||||
ASSERT_FALSE(element.is<std::string_view>());
|
||||
ASSERT_FALSE(element.is<const char *>());
|
||||
ASSERT_TRUE(element.is<int64_t>());
|
||||
if (expected_value < 0) {
|
||||
ASSERT_FALSE(element.is<uint64_t>());
|
||||
} else {
|
||||
ASSERT_TRUE(element.is<uint64_t>());
|
||||
}
|
||||
ASSERT_TRUE(element.is<double>());
|
||||
ASSERT_FALSE(element.is<bool>());
|
||||
ASSERT_FALSE(element.is_null());
|
||||
|
||||
// Test element.type()
|
||||
ASSERT_EQUAL(element.type(), expected_type);
|
||||
|
||||
// Test element.get<T>()
|
||||
int64_t value;
|
||||
result.get<int64_t>().tie(value, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(value, expected_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_uint64(const char *key, uint64_t expected_value) {
|
||||
std::cout << "Running " << __func__ << "(" << key << ")" << std::endl;
|
||||
|
||||
const auto expected_type = dom::element_type::UINT64;
|
||||
|
||||
dom::parser parser;
|
||||
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
|
||||
ASSERT_SUCCESS(result.error());
|
||||
|
||||
// Test simdjson_result<element>.is<T>() (error chain)
|
||||
ASSERT_FALSE(result.is<dom::array>());
|
||||
ASSERT_FALSE(result.is<dom::object>());
|
||||
ASSERT_FALSE(result.is<std::string_view>());
|
||||
ASSERT_FALSE(result.is<const char *>());
|
||||
ASSERT_FALSE(result.is<int64_t>());
|
||||
ASSERT_TRUE(result.is<uint64_t>());
|
||||
ASSERT_TRUE(result.is<double>());
|
||||
ASSERT_FALSE(result.is<bool>());
|
||||
ASSERT_FALSE(result.is_null());
|
||||
|
||||
// Test simdjson_result<element>.type() (error chain)
|
||||
simdjson::error_code error;
|
||||
dom::element_type type;
|
||||
result.type().tie(type, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(type, expected_type);
|
||||
|
||||
// Test element.is<T>()
|
||||
dom::element element = result.first;
|
||||
ASSERT_FALSE(element.is<dom::array>());
|
||||
ASSERT_FALSE(element.is<dom::object>());
|
||||
ASSERT_FALSE(element.is<std::string_view>());
|
||||
ASSERT_FALSE(element.is<const char *>());
|
||||
ASSERT_FALSE(element.is<int64_t>());
|
||||
ASSERT_TRUE(element.is<uint64_t>());
|
||||
ASSERT_TRUE(element.is<double>());
|
||||
ASSERT_FALSE(element.is<bool>());
|
||||
ASSERT_FALSE(element.is_null());
|
||||
|
||||
// Test element.type()
|
||||
ASSERT_EQUAL(element.type(), expected_type);
|
||||
|
||||
// Test element.get<T>()
|
||||
uint64_t value;
|
||||
result.get<uint64_t>().tie(value, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(value, expected_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_double(const char *key, double expected_value) {
|
||||
std::cout << "Running " << __func__ << "(" << key << ")" << std::endl;
|
||||
|
||||
const auto expected_type = dom::element_type::DOUBLE;
|
||||
|
||||
dom::parser parser;
|
||||
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
|
||||
ASSERT_SUCCESS(result.error());
|
||||
|
||||
// Test simdjson_result<element>.is<T>() (error chain)
|
||||
ASSERT_FALSE(result.is<dom::array>());
|
||||
ASSERT_FALSE(result.is<dom::object>());
|
||||
ASSERT_FALSE(result.is<std::string_view>());
|
||||
ASSERT_FALSE(result.is<const char *>());
|
||||
ASSERT_FALSE(result.is<int64_t>());
|
||||
ASSERT_FALSE(result.is<uint64_t>());
|
||||
ASSERT_TRUE(result.is<double>());
|
||||
ASSERT_FALSE(result.is<bool>());
|
||||
ASSERT_FALSE(result.is_null());
|
||||
|
||||
// Test simdjson_result<element>.type() (error chain)
|
||||
simdjson::error_code error;
|
||||
dom::element_type type;
|
||||
result.type().tie(type, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(type, expected_type);
|
||||
|
||||
// Test element.is<T>()
|
||||
dom::element element = result.first;
|
||||
ASSERT_FALSE(element.is<dom::array>());
|
||||
ASSERT_FALSE(element.is<dom::object>());
|
||||
ASSERT_FALSE(element.is<std::string_view>());
|
||||
ASSERT_FALSE(element.is<const char *>());
|
||||
ASSERT_FALSE(element.is<int64_t>());
|
||||
ASSERT_FALSE(element.is<uint64_t>());
|
||||
ASSERT_TRUE(element.is<double>());
|
||||
ASSERT_FALSE(element.is<bool>());
|
||||
ASSERT_FALSE(element.is_null());
|
||||
|
||||
// Test element.type()
|
||||
ASSERT_EQUAL(element.type(), expected_type);
|
||||
|
||||
// Test element.get<T>()
|
||||
double value;
|
||||
result.get<double>().tie(value, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(value, expected_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_bool(const char *key, bool expected_value) {
|
||||
std::cout << "Running " << __func__ << "(" << key << ")" << std::endl;
|
||||
|
||||
const auto expected_type = dom::element_type::BOOL;
|
||||
|
||||
dom::parser parser;
|
||||
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
|
||||
ASSERT_SUCCESS(result.error());
|
||||
|
||||
// Test simdjson_result<element>.is<T>() (error chain)
|
||||
ASSERT_FALSE(result.is<dom::array>());
|
||||
ASSERT_FALSE(result.is<dom::object>());
|
||||
ASSERT_FALSE(result.is<std::string_view>());
|
||||
ASSERT_FALSE(result.is<const char *>());
|
||||
ASSERT_FALSE(result.is<int64_t>());
|
||||
ASSERT_FALSE(result.is<uint64_t>());
|
||||
ASSERT_FALSE(result.is<double>());
|
||||
ASSERT_TRUE(result.is<bool>());
|
||||
ASSERT_FALSE(result.is_null());
|
||||
|
||||
// Test simdjson_result<element>.type() (error chain)
|
||||
simdjson::error_code error;
|
||||
dom::element_type type;
|
||||
result.type().tie(type, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(type, expected_type);
|
||||
|
||||
// Test element.is<T>()
|
||||
dom::element element = result.first;
|
||||
ASSERT_FALSE(element.is<dom::array>());
|
||||
ASSERT_FALSE(element.is<dom::object>());
|
||||
ASSERT_FALSE(element.is<std::string_view>());
|
||||
ASSERT_FALSE(element.is<const char *>());
|
||||
ASSERT_FALSE(element.is<int64_t>());
|
||||
ASSERT_FALSE(element.is<uint64_t>());
|
||||
ASSERT_FALSE(element.is<double>());
|
||||
ASSERT_TRUE(element.is<bool>());
|
||||
ASSERT_FALSE(element.is_null());
|
||||
|
||||
// Test element.type()
|
||||
ASSERT_EQUAL(element.type(), expected_type);
|
||||
|
||||
// Test element.get<T>()
|
||||
bool value;
|
||||
result.get<bool>().tie(value, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(value, expected_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_null() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
|
||||
const auto expected_type = dom::element_type::NULL_VALUE;
|
||||
|
||||
dom::parser parser;
|
||||
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)["null"];
|
||||
ASSERT_SUCCESS(result.error());
|
||||
|
||||
// Test simdjson_result<element>.is<T>() (error chain)
|
||||
ASSERT_FALSE(result.is<dom::array>());
|
||||
ASSERT_FALSE(result.is<dom::object>());
|
||||
ASSERT_FALSE(result.is<std::string_view>());
|
||||
ASSERT_FALSE(result.is<const char *>());
|
||||
ASSERT_FALSE(result.is<int64_t>());
|
||||
ASSERT_FALSE(result.is<uint64_t>());
|
||||
ASSERT_FALSE(result.is<double>());
|
||||
ASSERT_FALSE(result.is<bool>());
|
||||
ASSERT_TRUE(result.is_null());
|
||||
|
||||
// Test simdjson_result<element>.type() (error chain)
|
||||
simdjson::error_code error;
|
||||
dom::element_type type;
|
||||
result.type().tie(type, error);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(type, expected_type);
|
||||
|
||||
// Test element.is<T>()
|
||||
dom::element element = result.first;
|
||||
ASSERT_FALSE(element.is<dom::array>());
|
||||
ASSERT_FALSE(element.is<dom::object>());
|
||||
ASSERT_FALSE(element.is<std::string_view>());
|
||||
ASSERT_FALSE(element.is<const char *>());
|
||||
ASSERT_FALSE(element.is<int64_t>());
|
||||
ASSERT_FALSE(element.is<uint64_t>());
|
||||
ASSERT_FALSE(element.is<double>());
|
||||
ASSERT_FALSE(element.is<bool>());
|
||||
ASSERT_TRUE(element.is_null());
|
||||
|
||||
// Test element.type()
|
||||
ASSERT_EQUAL(element.type(), expected_type);
|
||||
|
||||
// Test element.get<T>()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_array() &&
|
||||
|
||||
test_object() &&
|
||||
|
||||
test_string() &&
|
||||
|
||||
test_int64("0", 0) &&
|
||||
test_int64("1", 1) &&
|
||||
test_int64("-1", -1) &&
|
||||
test_int64("9223372036854775807", 9223372036854775807LL) &&
|
||||
test_int64("-9223372036854775808", -1 - 9223372036854775807LL) &&
|
||||
|
||||
test_uint64("9223372036854775808", 9223372036854775808ULL) &&
|
||||
test_uint64("18446744073709551615", 18446744073709551615ULL) &&
|
||||
|
||||
test_double("0.0", 0.0) &&
|
||||
test_double("0.1", 0.1) &&
|
||||
test_double("1e0", 1e0) &&
|
||||
test_double("1e100", 1e100) &&
|
||||
|
||||
test_bool("true", true) &&
|
||||
test_bool("false", false) &&
|
||||
|
||||
test_null() &&
|
||||
|
||||
true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace format_tests {
|
||||
using namespace simdjson;
|
||||
using namespace simdjson::dom;
|
||||
|
@ -1378,6 +1865,7 @@ int main(int argc, char *argv[]) {
|
|||
std::cout << "Running basic tests." << std::endl;
|
||||
if (parse_api_tests::run() &&
|
||||
dom_api_tests::run() &&
|
||||
type_tests::run() &&
|
||||
format_tests::run() &&
|
||||
document_tests::run() &&
|
||||
number_tests::run() &&
|
||||
|
|
|
@ -64,6 +64,52 @@ void basics_dom_2() {
|
|||
cout << cars.at("0/tire_pressure/1") << endl; // Prints 39.9}
|
||||
}
|
||||
|
||||
namespace treewalk_1 {
|
||||
void print_json(dom::element element) {
|
||||
switch (element.type()) {
|
||||
case dom::element_type::ARRAY:
|
||||
cout << "[";
|
||||
for (dom::element child : dom::array(element)) {
|
||||
print_json(child);
|
||||
cout << ",";
|
||||
}
|
||||
cout << "]";
|
||||
break;
|
||||
case dom::element_type::OBJECT:
|
||||
cout << "{";
|
||||
for (dom::key_value_pair field : dom::object(element)) {
|
||||
cout << "\"" << field.key << "\": ";
|
||||
print_json(field.value);
|
||||
}
|
||||
cout << "}";
|
||||
break;
|
||||
case dom::element_type::INT64:
|
||||
cout << int64_t(element) << endl;
|
||||
break;
|
||||
case dom::element_type::UINT64:
|
||||
cout << uint64_t(element) << endl;
|
||||
break;
|
||||
case dom::element_type::DOUBLE:
|
||||
cout << double(element) << endl;
|
||||
break;
|
||||
case dom::element_type::STRING:
|
||||
cout << std::string_view(element) << endl;
|
||||
break;
|
||||
case dom::element_type::BOOL:
|
||||
cout << bool(element) << endl;
|
||||
break;
|
||||
case dom::element_type::NULL_VALUE:
|
||||
cout << "null" << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void basics_treewalk_1() {
|
||||
dom::parser parser;
|
||||
print_json(parser.load("twitter.json"));
|
||||
}
|
||||
}
|
||||
|
||||
void basics_ndjson() {
|
||||
dom::parser parser;
|
||||
for (dom::element doc : parser.load_many("x.txt")) {
|
||||
|
|
Loading…
Reference in New Issue