Merge pull request #669 from simdjson/jkeiser/type-switch

Add element.type() for type switching
This commit is contained in:
John Keiser 2020-04-02 15:01:51 -07:00 committed by GitHub
commit c5e21a2469
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 732 additions and 62 deletions

View File

@ -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
----------------------------------------------

View File

@ -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;

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)) {}
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 {

View File

@ -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() &&

View File

@ -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")) {