mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Verification and fix for issue 1063 (JSON Pointers) (#1064)
* Specification is not followed. * Fixes. * Do not pass string_view by reference. * Better documentation. * The example is written for exceptions. * Better documentation. * Updating with deprecation. * Updating example. * Updating example.
This commit is contained in:
+143
-31
@@ -1,3 +1,9 @@
|
||||
/***************
|
||||
* We refer the programmer to
|
||||
* JavaScript Object Notation (JSON) Pointer
|
||||
* https://tools.ietf.org/html/rfc6901
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "simdjson.h"
|
||||
@@ -15,6 +21,35 @@
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
bool demo() {
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
std::cout << "demo test" << std::endl;
|
||||
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;
|
||||
dom::parser parser;
|
||||
dom::element cars = parser.parse(cars_json);
|
||||
double x = cars.at_pointer("/0/tire_pressure/1");
|
||||
if(x != 39.9) return false;
|
||||
// Iterating through an array of objects
|
||||
std::vector<double> measured;
|
||||
for (dom::element car_element : cars) {
|
||||
dom::object car;
|
||||
simdjson::error_code error;
|
||||
if ((error = car_element.get(car))) { std::cerr << error << std::endl; return false; }
|
||||
double x3 = car.at_pointer("/tire_pressure/1");
|
||||
measured.push_back(x3);
|
||||
}
|
||||
std::vector<double> expected = {39.9, 31, 30};
|
||||
if(measured != expected) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
const padded_string TEST_JSON = R"(
|
||||
{
|
||||
"/~01abc": [
|
||||
@@ -33,49 +68,126 @@ const padded_string TEST_JSON = R"(
|
||||
}
|
||||
)"_padded;
|
||||
|
||||
bool json_pointer_success_test(const char *json_pointer, std::string_view expected_value) {
|
||||
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 json_pointer_success_test(const padded_string & source, const char *json_pointer, std::string_view expected_value) {
|
||||
std::cout << "Running successful JSON pointer test '" << json_pointer << "' ..." << std::endl;
|
||||
dom::parser parser;
|
||||
std::string_view value;
|
||||
ASSERT_SUCCESS( parser.parse(TEST_JSON).at(json_pointer).get(value) );
|
||||
ASSERT_EQUAL(value, expected_value);
|
||||
dom::element doc;
|
||||
auto error = parser.parse(source).get(doc);
|
||||
if(error) { std::cerr << "cannot parse: " << error << std::endl; return false; }
|
||||
dom::element answer;
|
||||
error = doc.at_pointer(json_pointer).get(answer);
|
||||
if(error) { std::cerr << "cannot access pointer: " << error << std::endl; return false; }
|
||||
std::string str_answer = simdjson::minify(answer);
|
||||
if(str_answer != expected_value) {
|
||||
std::cerr << "They differ!!!" << std::endl;
|
||||
std::cerr << " found '" << str_answer << "'" << std::endl;
|
||||
std::cerr << " expected '" << expected_value << "'" << std::endl;
|
||||
}
|
||||
ASSERT_EQUAL(str_answer, expected_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool json_pointer_success_test(const char *json_pointer) {
|
||||
std::cout << "Running successful JSON pointer test '" << json_pointer << "' ..." << std::endl;
|
||||
dom::parser parser;
|
||||
ASSERT_SUCCESS( parser.parse(TEST_JSON).at(json_pointer).error() );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool json_pointer_failure_test(const char *json_pointer, error_code expected_error) {
|
||||
bool json_pointer_failure_test(const padded_string & source, const char *json_pointer, error_code expected_error) {
|
||||
std::cout << "Running invalid JSON pointer test '" << json_pointer << "' ..." << std::endl;
|
||||
dom::parser parser;
|
||||
ASSERT_ERROR(parser.parse(TEST_JSON).at(json_pointer).error(), expected_error);
|
||||
ASSERT_ERROR(parser.parse(source).at_pointer(json_pointer).error(), expected_error);
|
||||
return true;
|
||||
}
|
||||
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
// for pre 0.4 users (not standard compliant)
|
||||
bool legacy_support() {
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
std::cout << "legacy test" << std::endl;
|
||||
auto legacy_json = R"({"key": "value", "array": [0, 1, 2]})"_padded;
|
||||
dom::parser parser;
|
||||
dom::element legacy = parser.parse(legacy_json);
|
||||
std::string_view value_str = legacy.at("key");
|
||||
ASSERT_EQUAL(value_str, "value");
|
||||
int64_t array0 = legacy.at("array/0");
|
||||
ASSERT_EQUAL(array0, 0);
|
||||
array0 = legacy.at("array").at("0");
|
||||
ASSERT_EQUAL(array0, 0);
|
||||
ASSERT_ERROR(legacy.at("no_such_key").error(), NO_SUCH_FIELD);
|
||||
ASSERT_ERROR(legacy.at("array/9").error(), INDEX_OUT_OF_BOUNDS);
|
||||
ASSERT_ERROR(legacy.at("array/not_a_num").error(), INCORRECT_TYPE);
|
||||
ASSERT_ERROR(legacy.at("array/").error(), INVALID_JSON_POINTER);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
|
||||
// for 0.5 version and following (standard compliant)
|
||||
bool modern_support() {
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
std::cout << "modern test" << std::endl;
|
||||
auto example_json = R"({"key": "value", "array": [0, 1, 2]})"_padded;
|
||||
dom::parser parser;
|
||||
dom::element example = parser.parse(example_json);
|
||||
std::string_view value_str = example.at_pointer("/key");
|
||||
ASSERT_EQUAL(value_str, "value");
|
||||
int64_t array0 = example.at_pointer("/array/0");
|
||||
ASSERT_EQUAL(array0, 0);
|
||||
array0 = example.at_pointer("/array").at_pointer("/0");
|
||||
ASSERT_EQUAL(array0, 0);
|
||||
ASSERT_ERROR(example.at_pointer("/no_such_key").error(), NO_SUCH_FIELD);
|
||||
ASSERT_ERROR(example.at_pointer("/array/9").error(), INDEX_OUT_OF_BOUNDS);
|
||||
ASSERT_ERROR(example.at_pointer("/array/not_a_num").error(), INCORRECT_TYPE);
|
||||
ASSERT_ERROR(example.at_pointer("/array/").error(), INVALID_JSON_POINTER);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (true
|
||||
&& json_pointer_success_test("")
|
||||
&& json_pointer_success_test("~1~001abc")
|
||||
&& json_pointer_success_test("~1~001abc/1")
|
||||
&& json_pointer_success_test("~1~001abc/1/\\\" 0")
|
||||
&& json_pointer_success_test("~1~001abc/1/\\\" 0/0", "value0")
|
||||
&& json_pointer_success_test("~1~001abc/1/\\\" 0/1", "value1")
|
||||
&& json_pointer_failure_test("~1~001abc/1/\\\" 0/2", INDEX_OUT_OF_BOUNDS) // index actually out of bounds
|
||||
&& json_pointer_success_test("arr") // get array
|
||||
&& json_pointer_failure_test("arr/0", INDEX_OUT_OF_BOUNDS) // array index 0 out of bounds on empty array
|
||||
&& json_pointer_success_test("~1~001abc") // get object
|
||||
&& json_pointer_success_test("0", "0 ok") // object index with integer-ish key
|
||||
&& json_pointer_success_test("01", "01 ok") // object index with key that would be an invalid integer
|
||||
&& json_pointer_success_test("", "empty ok") // object index with empty key
|
||||
&& json_pointer_failure_test("~01abc", NO_SUCH_FIELD) // Test that we don't try to compare the literal key
|
||||
&& json_pointer_failure_test("~1~001abc/01", INVALID_JSON_POINTER) // Leading 0 in integer index
|
||||
&& json_pointer_failure_test("~1~001abc/", INVALID_JSON_POINTER) // Empty index to array
|
||||
&& json_pointer_failure_test("~1~001abc/-", INDEX_OUT_OF_BOUNDS) // End index is always out of bounds
|
||||
&& demo()
|
||||
&& legacy_support()
|
||||
&& modern_support()
|
||||
&& json_pointer_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})")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/foo", "[\"bar\",\"baz\"]")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/foo/0", "\"bar\"")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/", "0")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/a~1b", "1")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/c%d", "2")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/e^f", "3")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/g|h", "4")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/i\\j", "5")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/k\"l", "6")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/ ", "7")
|
||||
&& json_pointer_success_test(TEST_RFC_JSON, "/m~0n", "8")
|
||||
&& json_pointer_success_test(TEST_JSON, "",R"({"/~01abc":[0,{"\\\" 0":["value0","value1"]}],"0":"0 ok","01":"01 ok","":"empty ok","arr":[]})")
|
||||
&& json_pointer_success_test(TEST_JSON, "/~1~001abc",R"([0,{"\\\" 0":["value0","value1"]}])")
|
||||
&& json_pointer_success_test(TEST_JSON, "/~1~001abc/1",R"({"\\\" 0":["value0","value1"]})")
|
||||
&& json_pointer_success_test(TEST_JSON, "/~1~001abc/1/\\\" 0",R"(["value0","value1"])")
|
||||
&& json_pointer_success_test(TEST_JSON, "/~1~001abc/1/\\\" 0/0", "\"value0\"")
|
||||
&& json_pointer_success_test(TEST_JSON, "/~1~001abc/1/\\\" 0/1", "\"value1\"")
|
||||
&& json_pointer_failure_test(TEST_JSON, "/~1~001abc/1/\\\" 0/2", INDEX_OUT_OF_BOUNDS) // index actually out of bounds
|
||||
&& json_pointer_success_test(TEST_JSON, "/arr", R"([])") // get array
|
||||
&& json_pointer_failure_test(TEST_JSON, "/arr/0", INDEX_OUT_OF_BOUNDS) // array index 0 out of bounds on empty array
|
||||
&& json_pointer_failure_test(TEST_JSON, "~1~001abc", INVALID_JSON_POINTER)
|
||||
&& json_pointer_success_test(TEST_JSON, "/0", "\"0 ok\"") // object index with integer-ish key
|
||||
&& json_pointer_success_test(TEST_JSON, "/01", "\"01 ok\"") // object index with key that would be an invalid integer
|
||||
&& json_pointer_success_test(TEST_JSON, "", R"({"/~01abc":[0,{"\\\" 0":["value0","value1"]}],"0":"0 ok","01":"01 ok","":"empty ok","arr":[]})") // object index with empty key
|
||||
&& json_pointer_failure_test(TEST_JSON, "/~01abc", NO_SUCH_FIELD) // Test that we don't try to compare the literal key
|
||||
&& json_pointer_failure_test(TEST_JSON, "/~1~001abc/01", INVALID_JSON_POINTER) // Leading 0 in integer index
|
||||
&& json_pointer_failure_test(TEST_JSON, "/~1~001abc/", INVALID_JSON_POINTER) // Empty index to array
|
||||
&& json_pointer_failure_test(TEST_JSON, "/~1~001abc/-", INDEX_OUT_OF_BOUNDS) // End index is always out of bounds
|
||||
) {
|
||||
std::cout << "Success!" << std::endl;
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user