Add element.type() for type switching

This commit is contained in:
John Keiser 2020-04-02 12:14:29 -07:00
parent 7dad9fca0f
commit 13aee51011
5 changed files with 732 additions and 62 deletions

View File

@ -10,6 +10,7 @@ An overview of what you need to know to use simdjson, with examples.
* [Error Handling](#error-handling) * [Error Handling](#error-handling)
* [Error Handling Example](#error-handling-example) * [Error Handling Example](#error-handling-example)
* [Exceptions](#exceptions) * [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) * [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
* [Thread Safety](#thread-safety) * [Thread Safety](#thread-safety)
@ -67,6 +68,8 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
first element. first element.
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a > 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. > 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: 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++ ```c++
auto cars_json = R"( [ auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] }, { "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": "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": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded; ] )"_padded;
dom::parser parser; dom::parser parser;
auto [cars, error] = parser.parse(cars_json).get<dom::array>(); 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 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. 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 Newline-Delimited JSON (ndjson) and JSON lines
---------------------------------------------- ----------------------------------------------

View File

@ -41,12 +41,11 @@ namespace simdjson::internal {
using namespace simdjson::dom; using namespace simdjson::dom;
constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF; constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF;
enum class tape_type;
class tape_ref; /**
/**
* The possible types in the tape. Internal only. * The possible types in the tape. Internal only.
*/ */
enum class tape_type { enum class tape_type {
ROOT = 'r', ROOT = 'r',
START_ARRAY = '[', START_ARRAY = '[',
START_OBJECT = '{', START_OBJECT = '{',
@ -59,17 +58,17 @@ class tape_ref;
TRUE_VALUE = 't', TRUE_VALUE = 't',
FALSE_VALUE = 'f', FALSE_VALUE = 'f',
NULL_VALUE = 'n' NULL_VALUE = 'n'
}; };
/** /**
* A reference to an element on the tape. Internal only. * A reference to an element on the tape. Internal only.
*/ */
class tape_ref { class tape_ref {
public: public:
really_inline tape_ref() noexcept; really_inline tape_ref() noexcept;
really_inline tape_ref(const document *doc, size_t json_index) noexcept; really_inline tape_ref(const document *doc, size_t json_index) noexcept;
inline size_t after_element() const noexcept; inline size_t after_element() const noexcept;
really_inline tape_type type() const noexcept; really_inline tape_type tape_ref_type() const noexcept;
really_inline uint64_t tape_value() const noexcept; really_inline uint64_t tape_value() const noexcept;
template<typename T> template<typename T>
really_inline T next_tape_value() const noexcept; really_inline T next_tape_value() const noexcept;
@ -80,11 +79,27 @@ class tape_ref;
/** The index of this element on `doc.tape[]` */ /** The index of this element on `doc.tape[]` */
size_t json_index; size_t json_index;
}; };
} // namespace simdjson::internal } // namespace simdjson::internal
namespace simdjson::dom { 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. * JSON array.
*/ */
@ -367,6 +382,9 @@ public:
/** Create a new, invalid element. */ /** Create a new, invalid element. */
really_inline element() noexcept; really_inline element() noexcept;
/** The type of this element. */
really_inline element_type type() const noexcept;
/** Whether this element is a json `null`. */ /** Whether this element is a json `null`. */
really_inline bool is_null() const noexcept; 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); } 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 } // namespace dom
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
@ -1172,6 +1220,7 @@ public:
really_inline simdjson_result(dom::element &&value) noexcept; ///< @private really_inline simdjson_result(dom::element &&value) noexcept; ///< @private
really_inline simdjson_result(error_code error) 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; inline simdjson_result<bool> is_null() const noexcept;
template<typename T> template<typename T>
inline simdjson_result<bool> is() const noexcept; inline simdjson_result<bool> is() const noexcept;

View File

@ -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)) {} : internal::simdjson_result_base<dom::element>(std::forward<dom::element>(value)) {}
really_inline simdjson_result<dom::element>::simdjson_result(error_code error) noexcept really_inline simdjson_result<dom::element>::simdjson_result(error_code error) noexcept
: internal::simdjson_result_base<dom::element>(error) {} : 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 { inline simdjson_result<bool> simdjson_result<dom::element>::is_null() const noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.is_null(); 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() noexcept : internal::tape_ref() {}
really_inline element::element(const document *_doc, size_t _json_index) noexcept : internal::tape_ref(_doc, _json_index) { } 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 { 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<> template<>
inline simdjson_result<bool> element::get<bool>() const noexcept { inline simdjson_result<bool> element::get<bool>() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case internal::tape_type::TRUE_VALUE: case internal::tape_type::TRUE_VALUE:
return true; return true;
case internal::tape_type::FALSE_VALUE: case internal::tape_type::FALSE_VALUE:
@ -732,7 +762,7 @@ inline simdjson_result<bool> element::get<bool>() const noexcept {
} }
template<> template<>
inline simdjson_result<const char *> element::get<const char *>() const noexcept { inline simdjson_result<const char *> element::get<const char *>() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case internal::tape_type::STRING: { case internal::tape_type::STRING: {
size_t string_buf_index = tape_value(); size_t string_buf_index = tape_value();
return reinterpret_cast<const char *>(&doc->string_buf[string_buf_index + sizeof(uint32_t)]); 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<> template<>
inline simdjson_result<std::string_view> element::get<std::string_view>() const noexcept { inline simdjson_result<std::string_view> element::get<std::string_view>() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case internal::tape_type::STRING: case internal::tape_type::STRING:
return get_string_view(); return get_string_view();
default: default:
@ -752,7 +782,7 @@ inline simdjson_result<std::string_view> element::get<std::string_view>() const
} }
template<> template<>
inline simdjson_result<uint64_t> element::get<uint64_t>() const noexcept { inline simdjson_result<uint64_t> element::get<uint64_t>() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case internal::tape_type::UINT64: case internal::tape_type::UINT64:
return next_tape_value<uint64_t>(); return next_tape_value<uint64_t>();
case internal::tape_type::INT64: { case internal::tape_type::INT64: {
@ -768,11 +798,11 @@ inline simdjson_result<uint64_t> element::get<uint64_t>() const noexcept {
} }
template<> template<>
inline simdjson_result<int64_t> element::get<int64_t>() const noexcept { inline simdjson_result<int64_t> element::get<int64_t>() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case internal::tape_type::UINT64: { case internal::tape_type::UINT64: {
uint64_t result = next_tape_value<uint64_t>(); 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 // 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 NUMBER_OUT_OF_RANGE;
} }
return static_cast<int64_t>(result); return static_cast<int64_t>(result);
@ -785,7 +815,7 @@ inline simdjson_result<int64_t> element::get<int64_t>() const noexcept {
} }
template<> template<>
inline simdjson_result<double> element::get<double>() const noexcept { inline simdjson_result<double> element::get<double>() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case internal::tape_type::UINT64: case internal::tape_type::UINT64:
return next_tape_value<uint64_t>(); return next_tape_value<uint64_t>();
case internal::tape_type::INT64: { case internal::tape_type::INT64: {
@ -804,7 +834,7 @@ inline simdjson_result<double> element::get<double>() const noexcept {
} }
template<> template<>
inline simdjson_result<array> element::get<array>() const noexcept { inline simdjson_result<array> element::get<array>() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case internal::tape_type::START_ARRAY: case internal::tape_type::START_ARRAY:
return array(doc, json_index); return array(doc, json_index);
default: default:
@ -813,7 +843,7 @@ inline simdjson_result<array> element::get<array>() const noexcept {
} }
template<> template<>
inline simdjson_result<object> element::get<object>() const noexcept { inline simdjson_result<object> element::get<object>() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case internal::tape_type::START_OBJECT: case internal::tape_type::START_OBJECT:
return object(doc, json_index); return object(doc, json_index);
default: 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 array() const noexcept(false) { return get<array>(); }
inline element::operator object() const noexcept(false) { return get<object>(); } 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(); 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(); return get<array>().end();
} }
@ -854,7 +884,7 @@ inline simdjson_result<element> element::operator[](const char *key) const noexc
return at_key(key); return at_key(key);
} }
inline simdjson_result<element> element::at(const std::string_view &json_pointer) const noexcept { 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: case internal::tape_type::START_OBJECT:
return object(doc, json_index).at(json_pointer); return object(doc, json_index).at(json_pointer);
case internal::tape_type::START_ARRAY: 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()) << "\":"; out << '"' << internal::escape_json_string(iter.get_string_view()) << "\":";
iter.json_index++; iter.json_index++;
} }
switch (iter.type()) { switch (iter.tape_ref_type()) {
// Arrays // Arrays
case tape_type::START_ARRAY: { case tape_type::START_ARRAY: {
@ -923,7 +953,7 @@ inline std::ostream& minify<dom::element>::print(std::ostream& out) {
iter.json_index++; iter.json_index++;
// Handle empty [] (we don't want to come back around and print commas) // 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 << ']'; out << ']';
depth--; depth--;
break; break;
@ -950,7 +980,7 @@ inline std::ostream& minify<dom::element>::print(std::ostream& out) {
iter.json_index++; iter.json_index++;
// Handle empty {} (we don't want to come back around and print commas) // 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 << '}'; out << '}';
depth--; depth--;
break; break;
@ -997,8 +1027,8 @@ inline std::ostream& minify<dom::element>::print(std::ostream& out) {
after_value = true; after_value = true;
// Handle multiple ends in a row // Handle multiple ends in a row
while (depth != 0 && (iter.type() == tape_type::END_ARRAY || iter.type() == tape_type::END_OBJECT)) { while (depth != 0 && (iter.tape_ref_type() == tape_type::END_ARRAY || iter.tape_ref_type() == tape_type::END_OBJECT)) {
out << char(iter.type()); out << char(iter.tape_ref_type());
depth--; depth--;
iter.json_index++; 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} {} 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 { inline size_t tape_ref::after_element() const noexcept {
switch (type()) { switch (tape_ref_type()) {
case tape_type::START_ARRAY: case tape_type::START_ARRAY:
case tape_type::START_OBJECT: case tape_type::START_OBJECT:
return tape_value(); return tape_value();
@ -1082,7 +1112,7 @@ inline size_t tape_ref::after_element() const noexcept {
return json_index + 1; 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); return static_cast<tape_type>(doc->tape[json_index] >> 56);
} }
really_inline uint64_t internal::tape_ref::tape_value() const noexcept { really_inline uint64_t internal::tape_ref::tape_value() const noexcept {

View File

@ -23,6 +23,11 @@
#define NDJSON_TEST_PATH "jsonexamples/amazon_cellphones.ndjson" #define NDJSON_TEST_PATH "jsonexamples/amazon_cellphones.ndjson"
#endif #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 { namespace number_tests {
// ulp distance // 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 { namespace format_tests {
using namespace simdjson; using namespace simdjson;
using namespace simdjson::dom; using namespace simdjson::dom;
@ -1378,6 +1865,7 @@ int main(int argc, char *argv[]) {
std::cout << "Running basic tests." << std::endl; std::cout << "Running basic tests." << std::endl;
if (parse_api_tests::run() && if (parse_api_tests::run() &&
dom_api_tests::run() && dom_api_tests::run() &&
type_tests::run() &&
format_tests::run() && format_tests::run() &&
document_tests::run() && document_tests::run() &&
number_tests::run() && number_tests::run() &&

View File

@ -64,6 +64,52 @@ void basics_dom_2() {
cout << cars.at("0/tire_pressure/1") << endl; // Prints 39.9} 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() { void basics_ndjson() {
dom::parser parser; dom::parser parser;
for (dom::element doc : parser.load_many("x.txt")) { for (dom::element doc : parser.load_many("x.txt")) {