Add JSON Pointer for On Demand (#1615)
* Add working JSON pointer for array of atoms. * Add working JSON pointer for object with key-atom pairs. * Add first version of JSON pointer. * Update tests (2 tests). * Make tests exceptionless. * Fix builing issues. * Add more tests. Add json_pointer validation in array-inl.h and object-inl.h and empty json_pointer in document-inl.h. * Fix errors in tests. * Review. * Add missing comment.
This commit is contained in:
parent
40cba172ed
commit
a4803d50c5
|
@ -93,6 +93,51 @@ simdjson_really_inline simdjson_result<size_t> array::count_elements() & noexcep
|
|||
return count;
|
||||
}
|
||||
|
||||
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
|
||||
json_pointer = json_pointer.substr(1);
|
||||
// - means "the append position" or "the element after the end of the array"
|
||||
// We don't support this, because we're returning a real element, not a position.
|
||||
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
|
||||
|
||||
// Read the array index
|
||||
size_t array_index = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) {
|
||||
uint8_t digit = uint8_t(json_pointer[i] - '0');
|
||||
// Check for non-digit in array index. If it's there, we're trying to get a field in an object
|
||||
if (digit > 9) { return INCORRECT_TYPE; }
|
||||
array_index = array_index*10 + digit;
|
||||
}
|
||||
|
||||
// 0 followed by other digits is invalid
|
||||
if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0"
|
||||
|
||||
// Empty string is invalid; so is a "/" with no digits before it
|
||||
if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index"
|
||||
// Get the child
|
||||
auto child = at(array_index);
|
||||
// If there is an error, it ends here
|
||||
if(child.error()) {
|
||||
return child;
|
||||
}
|
||||
|
||||
// If there is a /, we're not done yet, call recursively.
|
||||
if (i < json_pointer.length()) {
|
||||
child = child.at_pointer(json_pointer.substr(i));
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
simdjson_really_inline simdjson_result<value> array::at(size_t index) noexcept {
|
||||
size_t i=0;
|
||||
for (auto value : *this) {
|
||||
if (i == index) { return value; }
|
||||
i++;
|
||||
}
|
||||
return INDEX_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
@ -126,4 +171,8 @@ simdjson_really_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEME
|
|||
if (error()) { return error(); }
|
||||
return first.count_elements();
|
||||
}
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -43,6 +43,27 @@ public:
|
|||
* safe to continue.
|
||||
*/
|
||||
simdjson_really_inline simdjson_result<size_t> count_elements() & noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer. We use the RFC 6901
|
||||
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
|
||||
* as the root of its own JSON document.
|
||||
*
|
||||
* ondemand::parser parser;
|
||||
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("/0/foo/a/1") == 20
|
||||
*
|
||||
* Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
|
||||
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
|
||||
* @return The value associated with the given JSON pointer, or:
|
||||
* - NO_SUCH_FIELD if a field does not exist in an object
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
|
||||
* - INCORRECT_TYPE if a non-integer is used to access an array
|
||||
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
|
||||
*/
|
||||
inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Begin array iteration.
|
||||
|
@ -80,6 +101,15 @@ protected:
|
|||
*/
|
||||
simdjson_really_inline array(const value_iterator &iter) noexcept;
|
||||
|
||||
/**
|
||||
* Get the value at the given index. This function has linear-time complexity.
|
||||
* This function should only be called once as the array iterator is not reset between each call.
|
||||
*
|
||||
* @return The value at the given index, or:
|
||||
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
|
||||
*/
|
||||
simdjson_really_inline simdjson_result<value> at(size_t index) noexcept;
|
||||
|
||||
/**
|
||||
* Iterator marking current position.
|
||||
*
|
||||
|
@ -110,6 +140,7 @@ public:
|
|||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> begin() noexcept;
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> end() noexcept;
|
||||
simdjson_really_inline simdjson_result<size_t> count_elements() & noexcept;
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -134,6 +134,23 @@ simdjson_really_inline simdjson_result<std::string_view> document::raw_json_toke
|
|||
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_start_length());
|
||||
}
|
||||
|
||||
simdjson_really_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (json_pointer.empty()) {
|
||||
return this->resume_value();
|
||||
}
|
||||
json_type t;
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
switch (t)
|
||||
{
|
||||
case json_type::array:
|
||||
return (*this).get_array().at_pointer(json_pointer);
|
||||
case json_type::object:
|
||||
return (*this).get_object().at_pointer(json_pointer);
|
||||
default:
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
@ -311,4 +328,9 @@ simdjson_really_inline simdjson_result<std::string_view> simdjson_result<SIMDJSO
|
|||
return first.raw_json_token();
|
||||
}
|
||||
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -318,6 +318,34 @@ public:
|
|||
* Returns debugging information.
|
||||
*/
|
||||
inline std::string to_debug_string() noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer. We use the RFC 6901
|
||||
* https://tools.ietf.org/html/rfc6901 standard.
|
||||
*
|
||||
* ondemand::parser parser;
|
||||
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("/foo/a/1") == 20
|
||||
*
|
||||
* It is allowed for a key to be the empty string:
|
||||
*
|
||||
* ondemand::parser parser;
|
||||
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("//a/1") == 20
|
||||
*
|
||||
* Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
|
||||
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
|
||||
*
|
||||
* @return The value associated with the given JSON pointer, or:
|
||||
* - NO_SUCH_FIELD if a field does not exist in an object
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
|
||||
* - INCORRECT_TYPE if a non-integer is used to access an array
|
||||
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
|
||||
*/
|
||||
simdjson_really_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
|
||||
protected:
|
||||
simdjson_really_inline document(ondemand::json_iterator &&iter) noexcept;
|
||||
simdjson_really_inline const uint8_t *text(uint32_t idx) const noexcept;
|
||||
|
@ -396,6 +424,8 @@ public:
|
|||
|
||||
/** @copydoc simdjson_really_inline std::string_view document::raw_json_token() const noexcept */
|
||||
simdjson_really_inline simdjson_result<std::string_view> raw_json_token() noexcept;
|
||||
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -68,6 +68,46 @@ simdjson_really_inline simdjson_result<object_iterator> object::end() noexcept {
|
|||
return object_iterator(iter);
|
||||
}
|
||||
|
||||
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
|
||||
json_pointer = json_pointer.substr(1);
|
||||
size_t slash = json_pointer.find('/');
|
||||
std::string_view key = json_pointer.substr(0, slash);
|
||||
// Grab the child with the given key
|
||||
simdjson_result<value> child;
|
||||
|
||||
// If there is an escape character in the key, unescape it and then get the child.
|
||||
size_t escape = key.find('~');
|
||||
if (escape != std::string_view::npos) {
|
||||
// Unescape the key
|
||||
std::string unescaped(key);
|
||||
do {
|
||||
switch (unescaped[escape+1]) {
|
||||
case '0':
|
||||
unescaped.replace(escape, 2, "~");
|
||||
break;
|
||||
case '1':
|
||||
unescaped.replace(escape, 2, "/");
|
||||
break;
|
||||
default:
|
||||
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
|
||||
}
|
||||
escape = unescaped.find('~', escape+1);
|
||||
} while (escape != std::string::npos);
|
||||
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
|
||||
} else {
|
||||
child = find_field(key);
|
||||
}
|
||||
if(child.error()) {
|
||||
return child; // we do not continue if there was an error
|
||||
}
|
||||
// If there is a /, we have to recurse and look up more of the path
|
||||
if (slash != std::string_view::npos) {
|
||||
child = child.at_pointer(json_pointer.substr(slash));
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
@ -112,4 +152,9 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(key);
|
||||
}
|
||||
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -74,6 +74,34 @@ public:
|
|||
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
|
||||
simdjson_really_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer. We use the RFC 6901
|
||||
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
|
||||
* as the root of its own JSON document.
|
||||
*
|
||||
* ondemand::parser parser;
|
||||
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("/foo/a/1") == 20
|
||||
*
|
||||
* It is allowed for a key to be the empty string:
|
||||
*
|
||||
* ondemand::parser parser;
|
||||
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("//a/1") == 20
|
||||
*
|
||||
* Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
|
||||
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
|
||||
*
|
||||
* @return The value associated with the given JSON pointer, or:
|
||||
* - NO_SUCH_FIELD if a field does not exist in an object
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
|
||||
* - INCORRECT_TYPE if a non-integer is used to access an array
|
||||
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
|
||||
*/
|
||||
inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
|
||||
protected:
|
||||
static simdjson_really_inline simdjson_result<object> start(value_iterator &iter) noexcept;
|
||||
static simdjson_really_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
|
||||
|
@ -111,6 +139,7 @@ public:
|
|||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -135,6 +135,20 @@ simdjson_really_inline std::string_view value::raw_json_token() noexcept {
|
|||
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
|
||||
}
|
||||
|
||||
simdjson_really_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
|
||||
json_type t;
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
switch (t)
|
||||
{
|
||||
case json_type::array:
|
||||
return (*this).get_array().at_pointer(json_pointer);
|
||||
case json_type::object:
|
||||
return (*this).get_object().at_pointer(json_pointer);
|
||||
default:
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
@ -296,4 +310,9 @@ simdjson_really_inline simdjson_result<std::string_view> simdjson_result<SIMDJSO
|
|||
return first.raw_json_token();
|
||||
}
|
||||
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -314,6 +314,33 @@ public:
|
|||
*/
|
||||
simdjson_really_inline std::string_view raw_json_token() noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer. We use the RFC 6901
|
||||
* https://tools.ietf.org/html/rfc6901 standard.
|
||||
*
|
||||
* ondemand::parser parser;
|
||||
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("/foo/a/1") == 20
|
||||
*
|
||||
* It is allowed for a key to be the empty string:
|
||||
*
|
||||
* ondemand::parser parser;
|
||||
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("//a/1") == 20
|
||||
*
|
||||
* Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
|
||||
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
|
||||
*
|
||||
* @return The value associated with the given JSON pointer, or:
|
||||
* - NO_SUCH_FIELD if a field does not exist in an object
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
|
||||
* - INCORRECT_TYPE if a non-integer is used to access an array
|
||||
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
|
||||
*/
|
||||
simdjson_really_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Create a value.
|
||||
|
@ -459,6 +486,8 @@ public:
|
|||
|
||||
/** @copydoc simdjson_really_inline std::string_view value::raw_json_token() const noexcept */
|
||||
simdjson_really_inline simdjson_result<std::string_view> raw_json_token() noexcept;
|
||||
|
||||
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
link_libraries(simdjson)
|
||||
include_directories(..)
|
||||
add_subdirectory(compilation_failure_tests)
|
||||
add_cpp_test(ondemand_tostring_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_tostring_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_active_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_array_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_array_error_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_compilation_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_error_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_json_pointer_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_key_string_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_misc_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_misc_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_number_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_object_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_object_error_tests LABELS ondemand acceptance per_implementation)
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
#include <string>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace json_pointer_tests {
|
||||
const padded_string TEST_JSON = R"(
|
||||
{
|
||||
"/~01abc": [
|
||||
0,
|
||||
{
|
||||
"\\\" 0": [
|
||||
"value0",
|
||||
"value1"
|
||||
]
|
||||
}
|
||||
],
|
||||
"0": "0 ok",
|
||||
"01": "01 ok",
|
||||
"": "empty ok",
|
||||
"arr": []
|
||||
}
|
||||
)"_padded;
|
||||
|
||||
const padded_string TEST_RFC_JSON = R"(
|
||||
{
|
||||
"foo": ["bar", "baz"],
|
||||
"": 0,
|
||||
"a/b": 1,
|
||||
"c%d": 2,
|
||||
"e^f": 3,
|
||||
"g|h": 4,
|
||||
"i\\j": 5,
|
||||
"k\"l": 6,
|
||||
" ": 7,
|
||||
"m~n": 8
|
||||
}
|
||||
)"_padded;
|
||||
|
||||
bool run_success_test(const padded_string & json,std::string_view json_pointer,std::string expected) {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
ondemand::value val;
|
||||
std::string actual;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
ASSERT_SUCCESS(doc.at_pointer(json_pointer).get(val));
|
||||
ASSERT_SUCCESS(simdjson::to_string(val).get(actual));
|
||||
ASSERT_EQUAL(actual,expected);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run_failure_test(const padded_string & json,std::string_view json_pointer,error_code expected) {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
ASSERT_EQUAL(doc.at_pointer(json_pointer).error(),expected);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool demo_test() {
|
||||
TEST_START();
|
||||
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 ] }
|
||||
] )"_padded;
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document cars;
|
||||
double x;
|
||||
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
|
||||
ASSERT_SUCCESS(cars.at_pointer("/0/tire_pressure/1").get(x));
|
||||
ASSERT_EQUAL(x,39.9);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool demo_relative_path() {
|
||||
TEST_START();
|
||||
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 ] }
|
||||
] )"_padded;
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document cars;
|
||||
std::vector<double> measured;
|
||||
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
|
||||
for (auto car_element : cars) {
|
||||
double x;
|
||||
ASSERT_SUCCESS(car_element.at_pointer("/tire_pressure/1").get(x));
|
||||
measured.push_back(x);
|
||||
}
|
||||
|
||||
std::vector<double> expected = {39.9, 31, 30};
|
||||
if (measured != expected) { return false; }
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool many_json_pointers() {
|
||||
TEST_START();
|
||||
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 ] }
|
||||
] )"_padded;
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document cars;
|
||||
std::vector<double> measured;
|
||||
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
double x;
|
||||
std::string json_pointer = "/" + std::to_string(i) + "/tire_pressure/1";
|
||||
ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(x));
|
||||
measured.push_back(x);
|
||||
cars.rewind();
|
||||
}
|
||||
|
||||
std::vector<double> expected = {39.9, 31, 30};
|
||||
if (measured != expected) { return false; }
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return
|
||||
demo_test() &&
|
||||
demo_relative_path() &&
|
||||
run_success_test(TEST_RFC_JSON,"",R"({"foo":["bar","baz"],"":0,"a/b":1,"c%d":2,"e^f":3,"g|h":4,"i\\j":5,"k\"l":6," ":7,"m~n":8})") &&
|
||||
run_success_test(TEST_RFC_JSON,"/foo",R"(["bar","baz"])") &&
|
||||
run_success_test(TEST_RFC_JSON,"/foo/0",R"("bar")") &&
|
||||
run_success_test(TEST_RFC_JSON,"/",R"(0)") &&
|
||||
run_success_test(TEST_RFC_JSON,"/a~1b",R"(1)") &&
|
||||
run_success_test(TEST_RFC_JSON,"/c%d",R"(2)") &&
|
||||
run_success_test(TEST_RFC_JSON,"/e^f",R"(3)") &&
|
||||
run_success_test(TEST_RFC_JSON,"/g|h",R"(4)") &&
|
||||
run_success_test(TEST_RFC_JSON,R"(/i\\j)",R"(5)") &&
|
||||
run_success_test(TEST_RFC_JSON,R"(/k\"l)",R"(6)") &&
|
||||
run_success_test(TEST_RFC_JSON,"/ ",R"(7)") &&
|
||||
run_success_test(TEST_RFC_JSON,"/m~0n",R"(8)") &&
|
||||
run_success_test(TEST_JSON, "", R"({"/~01abc":[0,{"\\\" 0":["value0","value1"]}],"0":"0 ok","01":"01 ok","":"empty ok","arr":[]})") &&
|
||||
run_success_test(TEST_JSON, R"(/~1~001abc)", R"([0,{"\\\" 0":["value0","value1"]}])") &&
|
||||
run_success_test(TEST_JSON, R"(/~1~001abc/1)", R"({"\\\" 0":["value0","value1"]})") &&
|
||||
run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0)", R"(["value0","value1"])") &&
|
||||
run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/0)", "\"value0\"") &&
|
||||
run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/1)", "\"value1\"") &&
|
||||
run_success_test(TEST_JSON, "/arr", R"([])") &&
|
||||
run_success_test(TEST_JSON, "/0", "\"0 ok\"") &&
|
||||
run_success_test(TEST_JSON, "/01", "\"01 ok\"") &&
|
||||
run_success_test(TEST_JSON, "", R"({"/~01abc":[0,{"\\\" 0":["value0","value1"]}],"0":"0 ok","01":"01 ok","":"empty ok","arr":[]})") &&
|
||||
run_failure_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/2)", INDEX_OUT_OF_BOUNDS) &&
|
||||
run_failure_test(TEST_JSON, "/arr/0", INDEX_OUT_OF_BOUNDS) &&
|
||||
run_failure_test(TEST_JSON, "~1~001abc", INVALID_JSON_POINTER) &&
|
||||
run_failure_test(TEST_JSON, "/~01abc", NO_SUCH_FIELD) &&
|
||||
run_failure_test(TEST_JSON, "/~1~001abc/01", INVALID_JSON_POINTER) &&
|
||||
run_failure_test(TEST_JSON, "/~1~001abc/", INVALID_JSON_POINTER) &&
|
||||
run_failure_test(TEST_JSON, "/~1~001abc/-", INDEX_OUT_OF_BOUNDS) &&
|
||||
many_json_pointers() &&
|
||||
true;
|
||||
}
|
||||
} // json_pointer_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, json_pointer_tests::run);
|
||||
}
|
Loading…
Reference in New Issue