diff --git a/doc/basics.md b/doc/basics.md index 6e9f403f..b37f3f9e 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -63,6 +63,10 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper * **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }` * **Object Iteration:** You can iterate through an object's fields, too: `for (auto [key, value] : object)` +* **Array Index:** To get at an array value by index, use the at() method: `array.at(0)` gets the + 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. Here are some examples of all of the above: @@ -98,10 +102,13 @@ for (dom::object car : cars) { } ``` + + JSON Pointer ------------ -The simdjson library also supports JSON pointer, letting you reach further down into the document: +The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the +at() method, letting you reach further down into the document in a single call: ```c++ auto cars_json = R"( [ @@ -111,7 +118,7 @@ auto cars_json = R"( [ ] )"_padded; dom::parser parser; dom::element cars = parser.parse(cars_json); -cout << cars["/0/tire_pressure/1"] << endl; // Prints 39.9 +cout << cars.at("0/tire_pressure/1") << endl; // Prints 39.9 ``` Error Handling diff --git a/include/simdjson/document.h b/include/simdjson/document.h index dd284ec4..66967a2f 100644 --- a/include/simdjson/document.h +++ b/include/simdjson/document.h @@ -251,38 +251,34 @@ public: #endif // SIMDJSON_EXCEPTIONS /** - * Get the value associated with the given JSON pointer. + * Get the value associated with the given key. + * + * The key will be matched against **unescaped** JSON: * * dom::parser parser; - * element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"); - * doc["/foo/a/1"] == 20 - * doc["/"]["foo"]["a"].at(1) == 20 - * doc[""]["foo"]["a"].at(1) == 20 + * parser.parse(R"({ "a\n": 1 })")["a\n"].get().value == 1 + * parser.parse(R"({ "a\n": 1 })")["a\\n"].get().error == NO_SUCH_FIELD * - * @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 + * @return The value associated with this field, or: + * - NO_SUCH_FIELD if the field does not exist in the object + * - INCORRECT_TYPE if this is not an object */ - inline simdjson_result operator[](const std::string_view &json_pointer) const noexcept; + inline simdjson_result operator[](const std::string_view &key) const noexcept; /** - * Get the value associated with the given JSON pointer. + * Get the value associated with the given key. + * + * The key will be matched against **unescaped** JSON: * * dom::parser parser; - * element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"); - * doc["/foo/a/1"] == 20 - * doc["/"]["foo"]["a"].at(1) == 20 - * doc[""]["foo"]["a"].at(1) == 20 + * parser.parse(R"({ "a\n": 1 })")["a\n"].get().value == 1 + * parser.parse(R"({ "a\n": 1 })")["a\\n"].get().error == NO_SUCH_FIELD * - * @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 + * @return The value associated with this field, or: + * - NO_SUCH_FIELD if the field does not exist in the object + * - INCORRECT_TYPE if this is not an object */ - inline simdjson_result operator[](const char *json_pointer) const noexcept; + inline simdjson_result operator[](const char *key) const noexcept; /** * Get the value associated with the given JSON pointer. @@ -390,38 +386,6 @@ public: */ inline iterator end() const noexcept; - /** - * Get the value associated with the given JSON pointer. - * - * dom::parser parser; - * array a = parser.parse(R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"); - * a["0/foo/a/1"] == 20 - * a["0"]["foo"]["a"].at(1) == 20 - * - * @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 operator[](const std::string_view &json_pointer) const noexcept; - - /** - * Get the value associated with the given JSON pointer. - * - * dom::parser parser; - * array a = parser.parse(R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"); - * a["0/foo/a/1"] == 20 - * a["0"]["foo"]["a"].at(1) == 20 - * - * @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 operator[](const char *json_pointer) const noexcept; - /** * Get the value associated with the given JSON pointer. * @@ -511,36 +475,34 @@ public: inline iterator end() const noexcept; /** - * Get the value associated with the given JSON pointer. + * Get the value associated with the given key. + * + * The key will be matched against **unescaped** JSON: * * dom::parser parser; - * object obj = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"); - * obj["foo/a/1"] == 20 - * obj["foo"]["a"].at(1) == 20 + * parser.parse(R"({ "a\n": 1 })")["a\n"].get().value == 1 + * parser.parse(R"({ "a\n": 1 })")["a\\n"].get().error == NO_SUCH_FIELD * - * @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 + * @return The value associated with this field, or: + * - NO_SUCH_FIELD if the field does not exist in the object + * - INCORRECT_TYPE if this is not an object */ - inline simdjson_result operator[](const std::string_view &json_pointer) const noexcept; + inline simdjson_result operator[](const std::string_view &key) const noexcept; /** - * Get the value associated with the given JSON pointer. + * Get the value associated with the given key. + * + * The key will be matched against **unescaped** JSON: * * dom::parser parser; - * object obj = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"); - * obj["foo/a/1"] == 20 - * obj["foo"]["a"].at(1) == 20 + * parser.parse(R"({ "a\n": 1 })")["a\n"].get().value == 1 + * parser.parse(R"({ "a\n": 1 })")["a\\n"].get().error == NO_SUCH_FIELD * - * @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 + * @return The value associated with this field, or: + * - NO_SUCH_FIELD if the field does not exist in the object + * - INCORRECT_TYPE if this is not an object */ - inline simdjson_result operator[](const char *json_pointer) const noexcept; + inline simdjson_result operator[](const char *key) const noexcept; /** * Get the value associated with the given JSON pointer. @@ -1467,8 +1429,8 @@ public: template inline simdjson_result get() const noexcept; - inline simdjson_result operator[](const std::string_view &json_pointer) const noexcept; - inline simdjson_result operator[](const char *json_pointer) const noexcept; + inline simdjson_result operator[](const std::string_view &key) const noexcept; + inline simdjson_result operator[](const char *key) const noexcept; inline simdjson_result at(const std::string_view &json_pointer) const noexcept; inline simdjson_result at(size_t index) const noexcept; inline simdjson_result at_key(const std::string_view &key) const noexcept; @@ -1494,8 +1456,6 @@ public: really_inline simdjson_result(dom::array value) noexcept; really_inline simdjson_result(error_code error) noexcept; - inline simdjson_result operator[](const std::string_view &json_pointer) const noexcept; - inline simdjson_result operator[](const char *json_pointer) const noexcept; inline simdjson_result at(const std::string_view &json_pointer) const noexcept; inline simdjson_result at(size_t index) const noexcept; @@ -1513,8 +1473,8 @@ public: really_inline simdjson_result(dom::object value) noexcept; really_inline simdjson_result(error_code error) noexcept; - inline simdjson_result operator[](const std::string_view &json_pointer) const noexcept; - inline simdjson_result operator[](const char *json_pointer) const noexcept; + inline simdjson_result operator[](const std::string_view &key) const noexcept; + inline simdjson_result operator[](const char *key) const noexcept; inline simdjson_result at(const std::string_view &json_pointer) const noexcept; inline simdjson_result at_key(const std::string_view &key) const noexcept; inline simdjson_result at_key_case_insensitive(const std::string_view &key) const noexcept; diff --git a/include/simdjson/inline/document.h b/include/simdjson/inline/document.h index 72e8bfa3..c54576c8 100644 --- a/include/simdjson/inline/document.h +++ b/include/simdjson/inline/document.h @@ -38,28 +38,28 @@ inline simdjson_result simdjson_result::get() const noexcept { return first.get(); } -inline simdjson_result simdjson_result::operator[](const std::string_view &json_pointer) const noexcept { - if (error()) { return *this; } - return first[json_pointer]; +inline simdjson_result simdjson_result::operator[](const std::string_view &key) const noexcept { + if (error()) { return error(); } + return first[key]; } -inline simdjson_result simdjson_result::operator[](const char *json_pointer) const noexcept { - if (error()) { return *this; } - return first[json_pointer]; +inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { + if (error()) { return error(); } + return first[key]; } inline simdjson_result simdjson_result::at(const std::string_view &json_pointer) const noexcept { - if (error()) { return *this; } + if (error()) { return error(); } return first.at(json_pointer); } inline simdjson_result simdjson_result::at(size_t index) const noexcept { - if (error()) { return *this; } + if (error()) { return error(); } return first.at(index); } inline simdjson_result simdjson_result::at_key(const std::string_view &key) const noexcept { - if (error()) { return *this; } + if (error()) { return error(); } return first.at_key(key); } inline simdjson_result simdjson_result::at_key_case_insensitive(const std::string_view &key) const noexcept { - if (error()) { return *this; } + if (error()) { return error(); } return first.at_key_case_insensitive(key); } @@ -115,14 +115,6 @@ inline dom::array::iterator simdjson_result::end() const noexcept(fa #endif // SIMDJSON_EXCEPTIONS -inline simdjson_result simdjson_result::operator[](const std::string_view &json_pointer) const noexcept { - if (error()) { return error(); } - return first.at(json_pointer); -} -inline simdjson_result simdjson_result::operator[](const char *json_pointer) const noexcept { - if (error()) { return error(); } - return first.at(json_pointer); -} inline simdjson_result simdjson_result::at(const std::string_view &json_pointer) const noexcept { if (error()) { return error(); } return first.at(json_pointer); @@ -142,13 +134,13 @@ really_inline simdjson_result::simdjson_result(dom::object value) n really_inline simdjson_result::simdjson_result(error_code error) noexcept : internal::simdjson_result_base(error) {} -inline simdjson_result simdjson_result::operator[](const std::string_view &json_pointer) const noexcept { +inline simdjson_result simdjson_result::operator[](const std::string_view &key) const noexcept { if (error()) { return error(); } - return first[json_pointer]; + return first[key]; } -inline simdjson_result simdjson_result::operator[](const char *json_pointer) const noexcept { +inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { if (error()) { return error(); } - return first[json_pointer]; + return first[key]; } inline simdjson_result simdjson_result::at(const std::string_view &json_pointer) const noexcept { if (error()) { return error(); } @@ -603,11 +595,11 @@ inline object::iterator object::end() const noexcept { return iterator(doc, after_element() - 1); } -inline simdjson_result object::operator[](const std::string_view &json_pointer) const noexcept { - return at(json_pointer); +inline simdjson_result object::operator[](const std::string_view &key) const noexcept { + return at_key(key); } -inline simdjson_result object::operator[](const char *json_pointer) const noexcept { - return at(json_pointer); +inline simdjson_result object::operator[](const char *key) const noexcept { + return at_key(key); } inline simdjson_result object::at(const std::string_view &json_pointer) const noexcept { size_t slash = json_pointer.find('/'); @@ -840,11 +832,11 @@ inline element::operator object() const noexcept(false) { return get(); #endif -inline simdjson_result element::operator[](const std::string_view &json_pointer) const noexcept { - return at(json_pointer); +inline simdjson_result element::operator[](const std::string_view &key) const noexcept { + return at_key(key); } -inline simdjson_result element::operator[](const char *json_pointer) const noexcept { - return at(json_pointer); +inline simdjson_result element::operator[](const char *key) const noexcept { + return at_key(key); } inline simdjson_result element::at(const std::string_view &json_pointer) const noexcept { switch (type()) { diff --git a/tests/basictests.cpp b/tests/basictests.cpp index 56b9c7b8..d0547a8f 100644 --- a/tests/basictests.cpp +++ b/tests/basictests.cpp @@ -805,14 +805,14 @@ namespace dom_api_tests { bool document_object_index() { std::cout << "Running " << __func__ << std::endl; - string json(R"({ "a": 1, "b": 2, "c": 3})"); + string json(R"({ "a": 1, "b": 2, "c/d": 3})"); dom::parser parser; auto [doc, error] = parser.parse(json); if (doc["a"].get().first != 1) { cerr << "Expected uint64_t(doc[\"a\"]) to be 1, was " << doc["a"].first << endl; return false; } if (doc["b"].get().first != 2) { cerr << "Expected uint64_t(doc[\"b\"]) to be 2, was " << doc["b"].first << endl; return false; } - if (doc["c"].get().first != 3) { cerr << "Expected uint64_t(doc[\"c\"]) to be 3, was " << doc["c"].first << endl; return false; } + if (doc["c/d"].get().first != 3) { cerr << "Expected uint64_t(doc[\"c/d\"]) to be 3, was " << doc["c"].first << endl; return false; } // Check all three again in backwards order, to ensure we can go backwards - if (doc["c"].get().first != 3) { cerr << "Expected uint64_t(doc[\"c\"]) to be 3, was " << doc["c"].first << endl; return false; } + if (doc["c/d"].get().first != 3) { cerr << "Expected uint64_t(doc[\"c/d\"]) to be 3, was " << doc["c"].first << endl; return false; } if (doc["b"].get().first != 2) { cerr << "Expected uint64_t(doc[\"b\"]) to be 2, was " << doc["b"].first << endl; return false; } if (doc["a"].get().first != 1) { cerr << "Expected uint64_t(doc[\"a\"]) to be 1, was " << doc["a"].first << endl; return false; } @@ -825,7 +825,7 @@ namespace dom_api_tests { bool object_index() { std::cout << "Running " << __func__ << std::endl; - string json(R"({ "obj": { "a": 1, "b": 2, "c": 3 } })"); + string json(R"({ "obj": { "a": 1, "b": 2, "c/d": 3 } })"); dom::parser parser; auto [doc, error] = parser.parse(json); if (error) { cerr << "Error: " << error << endl; return false; } @@ -839,9 +839,9 @@ namespace dom_api_tests { obj["obj"].get().tie(obj, error); // tie(...) = fails with "no viable overloaded '='" on Apple clang version 11.0.0 if (obj["a"].get().first != 1) { cerr << "Expected uint64_t(obj[\"a\"]) to be 1, was " << obj["a"].first << endl; return false; } if (obj["b"].get().first != 2) { cerr << "Expected uint64_t(obj[\"b\"]) to be 2, was " << obj["b"].first << endl; return false; } - if (obj["c"].get().first != 3) { cerr << "Expected uint64_t(obj[\"c\"]) to be 3, was " << obj["c"].first << endl; return false; } + if (obj["c/d"].get().first != 3) { cerr << "Expected uint64_t(obj[\"c\"]) to be 3, was " << obj["c"].first << endl; return false; } // Check all three again in backwards order, to ensure we can go backwards - if (obj["c"].get().first != 3) { cerr << "Expected uint64_t(obj[\"c\"]) to be 3, was " << obj["c"].first << endl; return false; } + if (obj["c/d"].get().first != 3) { cerr << "Expected uint64_t(obj[\"c\"]) to be 3, was " << obj["c"].first << endl; return false; } if (obj["b"].get().first != 2) { cerr << "Expected uint64_t(obj[\"b\"]) to be 2, was " << obj["b"].first << endl; return false; } if (obj["a"].get().first != 1) { cerr << "Expected uint64_t(obj[\"a\"]) to be 1, was " << obj["a"].first << endl; return false; } diff --git a/tests/pointercheck.cpp b/tests/pointercheck.cpp index dbaf404e..1b3a719f 100644 --- a/tests/pointercheck.cpp +++ b/tests/pointercheck.cpp @@ -35,7 +35,7 @@ const padded_string TEST_JSON = R"( bool json_pointer_success_test(const char *json_pointer, std::string_view expected_value) { std::cout << "Running successful JSON pointer test '" << json_pointer << "' ..." << std::endl; dom::parser parser; - auto [value, error] = parser.parse(TEST_JSON)[json_pointer].get(); + auto [value, error] = parser.parse(TEST_JSON).at(json_pointer).get(); if (error) { std::cerr << "Unexpected Error: " << error << std::endl; return false; } ASSERT(value == expected_value); return true; @@ -44,7 +44,7 @@ bool json_pointer_success_test(const char *json_pointer, std::string_view expect bool json_pointer_success_test(const char *json_pointer) { std::cout << "Running successful JSON pointer test '" << json_pointer << "' ..." << std::endl; dom::parser parser; - auto [value, error] = parser.parse(TEST_JSON)[json_pointer]; + auto [value, error] = parser.parse(TEST_JSON).at(json_pointer); if (error) { std::cerr << "Unexpected Error: " << error << std::endl; return false; } return true; } @@ -53,7 +53,7 @@ bool json_pointer_success_test(const char *json_pointer) { bool json_pointer_failure_test(const char *json_pointer, error_code expected_failure_test) { std::cout << "Running invalid JSON pointer test '" << json_pointer << "' ..." << std::endl; dom::parser parser; - auto [value, error] = parser.parse(TEST_JSON)[json_pointer]; + auto [value, error] = parser.parse(TEST_JSON).at(json_pointer); ASSERT(error == expected_failure_test); return true; }