233 lines
9.2 KiB
C++
233 lines
9.2 KiB
C++
/***************
|
|
* We refer the programmer to
|
|
* JavaScript Object Notation (JSON) Pointer
|
|
* https://tools.ietf.org/html/rfc6901
|
|
*/
|
|
|
|
#include <iostream>
|
|
|
|
#include "simdjson.h"
|
|
#include "test_macros.h"
|
|
|
|
// we define our own asserts to get around NDEBUG
|
|
#ifndef ASSERT
|
|
#define ASSERT(x) \
|
|
{ if (!(x)) { \
|
|
std::cerr << "Failed assertion " << #x << std::endl; \
|
|
return false; \
|
|
} \
|
|
}
|
|
#endif
|
|
|
|
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": [
|
|
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 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;
|
|
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_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(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;
|
|
}
|
|
bool issue1142() {
|
|
#if SIMDJSON_EXCEPTIONS
|
|
std::cout << "issue 1142" << std::endl;
|
|
auto example_json = R"([1,2,{"1":"bla"}])"_padded;
|
|
dom::parser parser;
|
|
dom::element example = parser.parse(example_json);
|
|
auto e0 = dom::array(example).at(0).at_pointer("");
|
|
ASSERT_EQUAL(std::string("1"), simdjson::minify(e0))
|
|
auto o = dom::array(example).at(2).at_pointer("");
|
|
ASSERT_EQUAL(std::string(R"({"1":"bla"})"), simdjson::minify(o))
|
|
std::string_view s0 = dom::array(example).at(2).at_pointer("/1").at_pointer("");
|
|
if(s0 != "bla") {
|
|
std::cerr << s0 << std::endl;
|
|
return false;
|
|
}
|
|
auto example_json2 = R"("just a string")"_padded;
|
|
dom::element example2 = parser.parse(example_json2).at_pointer("");
|
|
if(std::string_view(example2) != "just a string") {
|
|
std::cerr << std::string_view(example2) << std::endl;
|
|
return false;
|
|
}
|
|
auto example_json3 = R"([])"_padded;
|
|
dom::element example3 = parser.parse(example_json3).at_pointer("");
|
|
ASSERT_EQUAL(std::string(R"([])"), simdjson::minify(example3));
|
|
|
|
const char * input_array = "[]";
|
|
size_t input_length = std::strlen(input_array);
|
|
auto element4 = parser.parse(input_array, input_length).at_pointer("");;
|
|
ASSERT_EQUAL(std::string(R"([])"), simdjson::minify(element4));
|
|
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
int main() {
|
|
if (true
|
|
&& demo()
|
|
&& issue1142()
|
|
&& 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;
|
|
} else {
|
|
std::cerr << "Failed!" << std::endl;
|
|
return 1;
|
|
}
|
|
}
|