Compare commits

...

62 Commits

Author SHA1 Message Date
Daniel Lemire 956661f800 removing printout 2025-08-05 18:40:37 -04:00
Daniel Lemire a306d2bd73 documentation 2025-08-05 17:27:38 -04:00
Daniel Lemire 9627831b5f some minor fixes 2025-08-05 17:02:13 -04:00
Joseph Olabisi 6c5ada32b9 remove unneccessary string allocation 2025-07-22 21:03:02 +01:00
Joseph Olabisi f576354342 fix jsonpathutil 2025-07-21 23:06:38 +01:00
Joseph Olabisi 1589d15361 fix 2025-07-21 22:34:17 +01:00
Joseph Olabisi c58f5dda2d minor changes 2025-07-21 21:45:31 +01:00
Joseph Olabisi 5753f54f6c fix boundary check and unnecessary string allocation 2025-07-21 21:31:47 +01:00
Joseph Olabisi f0cdafa857 add more tests 2025-07-21 21:15:38 +01:00
Joseph Olabisi 29ef75c783 fix bug with array, add boundary check to get_next_key_and_json_path function 2025-07-21 20:58:49 +01:00
Joseph Olabisi c3d45c7758 refactor process_json_path_of_child_element from recursion to loop in order to prevent stack overflow 2025-07-16 20:35:52 +01:00
Joseph Olabisi 3fda1b9c7d minor error handling 2025-07-14 01:35:19 +01:00
Joseph Olabisi fdcea9ad22 refactor and create util for getting key and json path 2025-07-14 01:15:02 +01:00
Joseph Olabisi 7b3168bfc8 add test for wildcard in nested array 2025-07-14 00:41:15 +01:00
Joseph Olabisi 3350ce97ba fix logic for wildcards inside arrays 2025-07-14 00:14:56 +01:00
Joseph Olabisi 61ec1fb659 fix bug in object-inl.h 2025-07-12 16:43:38 +01:00
Joseph Olabisi bfc8b89e6b final changes based on feedback - reduce string allocations 2025-07-10 17:10:25 +01:00
Joseph Olabisi 1382432686 address additional feedbacks (copilot) 2025-07-10 14:28:32 +01:00
Joseph Olabisi bebbf830fb address some feedbacks 2025-07-10 13:24:07 +01:00
Joseph Olabisi e7dda647e2 add test 2025-05-06 18:34:25 +01:00
Joseph Olabisi ec46aace3b nit 2025-05-06 18:27:00 +01:00
Joseph Olabisi 1dfe1bd350 nit 2025-05-06 18:24:14 +01:00
Joseph Olabisi 0b1d69938e cleanup 2025-05-06 18:22:32 +01:00
Joseph Olabisi 120e69f13c cleanup object-inl.h 2025-05-06 14:51:25 +01:00
Joseph Olabisi 14922fa6c4 revert object-inl.h 2025-05-06 14:37:50 +01:00
Joseph Olabisi 8fa2629c84 cleanup array-inl.h 2025-05-06 02:05:58 +01:00
Joseph Olabisi c51c2d2ce9 revert array-inl.h 2025-05-06 01:55:21 +01:00
Joseph Olabisi 3c9cde5ec1 restore quickstart.cpp 2025-05-06 01:47:28 +01:00
Joseph Olabisi 651c982d26 cleanup 2025-05-06 01:45:05 +01:00
Joseph Olabisi 3634cdbf94 restore examples/quickstart/CMakeLists.txt 2025-05-06 01:39:34 +01:00
Joseph Olabisi 3b47ae6a77 cleanup 2025-05-06 01:37:32 +01:00
Joseph Olabisi 07daf0a92e cleanup 2025-05-06 00:35:51 +01:00
Joseph Olabisi 9f74c0e958 cleanup 2025-05-06 00:29:22 +01:00
Joseph Olabisi 7a336e560c cleanup 2025-05-06 00:27:31 +01:00
Joseph Olabisi c6e7c89b59 modified: CMakeLists.txt 2025-05-06 00:23:16 +01:00
Joseph Olabisi ed838ffab9 cleanup 2 2025-05-06 00:21:35 +01:00
Joseph Olabisi 5981ca18e4 add some initial tests 2025-05-03 18:24:56 +01:00
Joseph Olabisi 1a11e19d07 cleanup 2025-05-03 13:46:11 +01:00
Joseph Olabisi 671a81f4e3 nit 2025-04-27 00:02:23 +01:00
Joseph Olabisi 008d89eb56 corrected logic 2025-04-26 21:01:05 +01:00
Joseph Olabisi a71655120a nit 2025-04-24 20:54:42 +01:00
Joseph Olabisi 531567ae94 approximately 30% improvement in runtime 2025-04-18 22:52:12 +01:00
Joseph Olabisi 46ae2d250f nit 2025-04-18 22:19:49 +01:00
Joseph Olabisi c35122bcb4 refactor to tail recursion 2025-04-18 21:43:44 +01:00
Joseph Olabisi f380604de5 minor improvements 2025-04-18 21:05:35 +01:00
Joseph Olabisi 00d01ea0f5 use memcmp 2025-04-15 11:22:43 +01:00
Joseph Olabisi 68bef810aa fix benchmark 2025-04-11 20:22:49 +01:00
Joseph Olabisi e328bad696 add benchmark 2025-04-11 10:11:42 +01:00
Joseph Olabisi c9ccb2d139 rename at_path_new to at_path_with_wildcard 2025-04-08 08:43:46 +01:00
Joseph Olabisi d99072982f handle array INCORRECT_TYPE 2025-04-05 20:52:53 +01:00
Joseph Olabisi 6b7f987cd5 add support for wildcard for arrays 2025-04-05 20:14:00 +01:00
Joseph Olabisi d32cd20ce6 fix key 2025-04-01 09:31:43 +01:00
Joseph Olabisi 2013a46518 done - handling child properties of wildcard result 2025-03-29 18:40:40 +00:00
Joseph Olabisi 6cb1911d3a wip - handling child properties of wildcard result 2025-03-26 10:31:58 +00:00
Joseph Olabisi 9ff7d2ccbe wip 2025-03-25 09:59:22 +00:00
Joseph Olabisi fa4a4ad8f0 wip - nested paths/pointers on wildcard results 2025-03-23 23:02:55 +00:00
Joseph Olabisi de6ad3c60c partially handle keys with wildcard 2025-03-21 22:31:47 +00:00
Joseph Olabisi 005133c555 wip - bruteforce surface wildcard with result 2025-03-18 02:20:30 +00:00
Joseph Olabisi 78025fdd64 wip brute-force wildcard for json_path 2025-03-11 21:16:26 +00:00
Dirk Eddelbuettel f3b034ac38 Address 'whitespace in literal' decreation warning from clang++-20 2025-03-06 14:28:01 -06:00
Daniel Lemire 9c2e8a8f39 upgrading uraimo/run-on-arch-action 2025-03-01 14:32:13 -05:00
yun dfa43f6cdd doc: replace ndjson[dot]org with ndjson spec (#2340)
the `ndjson[dot]org` expired, and point to incorrect website with malware.
more in https://github.com/ndjson/ndjson.github.io/issues/24

follow up #2234
2025-02-21 09:07:39 +00:00
19 changed files with 658 additions and 47 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2 - uses: uraimo/run-on-arch-action@v3
name: Test name: Test
id: runcmd id: runcmd
with: with:
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2 - uses: uraimo/run-on-arch-action@v3
name: Test name: Test
id: runcmd id: runcmd
with: with:
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2 - uses: uraimo/run-on-arch-action@v3
name: Test name: Test
id: runcmd id: runcmd
with: with:
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2 - uses: uraimo/run-on-arch-action@v3
name: Test name: Test
id: runcmd id: runcmd
with: with:
+1 -1
View File
@@ -1986,7 +1986,7 @@ serialize data into streams of multiple JSON documents. That is, instead of one
write out multiple records as independent JSON documents, to be read one-by-one. write out multiple records as independent JSON documents, to be read one-by-one.
The simdjson library also supports multithreaded JSON streaming through a large file The simdjson library also supports multithreaded JSON streaming through a large file
containing many smaller JSON documents in either [ndjson](http://ndjson.org) containing many smaller JSON documents in either [ndjson](https://github.com/ndjson/ndjson-spec)
or [JSON lines](http://jsonlines.org) format. If your JSON documents all contain arrays or [JSON lines](http://jsonlines.org) format. If your JSON documents all contain arrays
or objects, we even support direct file concatenation without whitespace. However, if there or objects, we even support direct file concatenation without whitespace. However, if there
is content between your JSON documents, it should be exclusively ASCII white-space characters. is content between your JSON documents, it should be exclusively ASCII white-space characters.
+68
View File
@@ -323,6 +323,74 @@ if(error) { /*won't happen*/ }
``` ```
## Using `at_path_with_wildcard` for JSONPath Queries
The `at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. This allows users to retrieve multiple elements from a JSON document in a single query. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the function returns an empty result. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield an empty set.
### Example Usage
Here is an example demonstrating the use of `at_path_with_wildcard`:
```cpp
simdjson::padded_string json_string = R"(
{
"firstName": "John",
"lastName": "doe",
"age": 26,
"address": {
"streetAddress": "naist street",
"city": "Nara",
"postalCode": "630-0192"
},
"phoneNumbers": [
{
"type": "iPhone",
"numbers": ["0123-4567-8888", "0123-4567-8788"]
},
{
"type": "home",
"numbers": ["0123-4567-8910"]
}
]
})"_padded;
dom::parser parser;
dom::element parsed_json = parser.parse(json_string);
std::vector<dom::element> values;
// Fetch all fields in the address object
auto error = parsed_json.at_path_with_wildcard("$.address.*").get(values);
if(error) {
// do something
}
for (auto &value : values) {
std::string_view field;
error = value.get(field);
if(error) {
// do something
}
std::cout << field << std::endl;
}
// Fetch all phone numbers
error = parsed_json.at_path_with_wildcard("$.phoneNumbers[*].numbers[*]").get(values);
if(error) {
// do something
}
for (auto &value : values) {
std::string_view number;
error = value.get(number);
if(error) {
// do something
}
std::cout << number << std::endl;
}
```
This function is particularly useful for extracting data from complex JSON structures with nested arrays and objects. By leveraging wildcards, you can simplify your queries and reduce the need for multiple iterations.
Error Handling Error Handling
-------------- --------------
+1 -1
View File
@@ -130,7 +130,7 @@ If your documents are all objects or arrays, then you may even have nothing betw
E.g., `[1,2]{"32":1}` is recognized as two documents. E.g., `[1,2]{"32":1}` is recognized as two documents.
Some official formats **(non-exhaustive list)**: Some official formats **(non-exhaustive list)**:
- [Newline-Delimited JSON (NDJSON)](http://ndjson.org/) - [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec/)
- [JSON lines (JSONL)](http://jsonlines.org/) - [JSON lines (JSONL)](http://jsonlines.org/)
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by JsonStream! - [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by JsonStream!
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming) - [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
+105
View File
@@ -52,11 +52,22 @@ inline simdjson_result<dom::element> simdjson_result<dom::array>::at_pointer(std
return at_pointer(json_pointer); return at_pointer(json_pointer);
} }
inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::array>::at_path_with_wildcard(std::string_view json_path) const noexcept {
if (error()) {
return error();
}
return first.at_path_with_wildcard(json_path);
}
inline simdjson_result<dom::element> simdjson_result<dom::array>::at(size_t index) const noexcept { inline simdjson_result<dom::element> simdjson_result<dom::array>::at(size_t index) const noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.at(index); return first.at(index);
} }
inline std::vector<dom::element>& simdjson_result<dom::array>::get_values(std::vector<dom::element>& out) const noexcept {
return first.get_values(out);
}
namespace dom { namespace dom {
// //
@@ -127,6 +138,91 @@ inline simdjson_result<element> array::at_path(std::string_view json_path) const
return at_pointer(json_pointer); return at_pointer(json_pointer);
} }
inline void array::process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept {
if (current == end) {
return;
}
simdjson_result<std::vector<element>> result;
for (auto it = current; it != end; ++it) {
result = it->at_path_with_wildcard(path_suffix);
if (!result.error()) {
std::vector<element> child_result = result.value();
accumulator.reserve(accumulator.size() + child_result.size());
accumulator.insert(accumulator.end(),
std::make_move_iterator(child_result.begin()),
std::make_move_iterator(child_result.end()));
}
}
}
inline simdjson_result<std::vector<element>> array::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i = 0;
// json_path.starts_with('$') requires C++20.
if (!json_path.empty() && json_path.front() == '$') {
i = 1;
}
if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) {
return INVALID_JSON_POINTER;
}
if (json_path.find("*") != std::string::npos) {
std::vector<element> child_values;
if (
(json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) ||
(json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2)
) {
get_values(child_values);
return child_values;
}
std::pair<std::string_view, std::string_view> key_and_json_path = get_next_key_and_json_path(json_path);
std::string_view key = key_and_json_path.first;
json_path = key_and_json_path.second;
if (key.size() > 0) {
if (key == "*") {
get_values(child_values);
} else {
auto pointer_result = at_pointer("/" + std::string(key));
if (!pointer_result.error()) {
child_values.emplace_back(pointer_result.value());
}
}
std::vector<element> result = {};
if (child_values.size() > 0) {
std::vector<element>::iterator child_values_begin = child_values.begin();
std::vector<element>::iterator child_values_end = child_values.end();
process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result);
}
return result;
} else {
return INVALID_JSON_POINTER;
}
} else {
auto result = at_path(json_path);
if (result.error()) {
return result.error();
}
return std::vector{std::move(result.value())};
}
}
inline simdjson_result<element> array::at(size_t index) const noexcept { inline simdjson_result<element> array::at(size_t index) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i=0; size_t i=0;
@@ -137,6 +233,15 @@ inline simdjson_result<element> array::at(size_t index) const noexcept {
return INDEX_OUT_OF_BOUNDS; return INDEX_OUT_OF_BOUNDS;
} }
inline std::vector<element>& array::get_values(std::vector<element>& out) const noexcept {
out.reserve(this->size());
for (auto element : *this) {
out.emplace_back(element);
}
return out;
}
inline array::operator element() const noexcept { inline array::operator element() const noexcept {
return element(tape); return element(tape);
} }
+25
View File
@@ -1,6 +1,8 @@
#ifndef SIMDJSON_DOM_ARRAY_H #ifndef SIMDJSON_DOM_ARRAY_H
#define SIMDJSON_DOM_ARRAY_H #define SIMDJSON_DOM_ARRAY_H
#include <vector>
#include "simdjson/dom/base.h" #include "simdjson/dom/base.h"
#include "simdjson/internal/tape_ref.h" #include "simdjson/internal/tape_ref.h"
@@ -108,6 +110,17 @@ public:
*/ */
inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept; inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept;
/**
* Recursive function which processes the json path of each child element
*/
inline void process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept;
/**
* Adds support for JSONPath expression with wildcards '*'
*/
inline simdjson_result<std::vector<element>> at_path_with_wildcard(std::string_view json_path) const noexcept;
/** /**
* Get the value associated with the given JSONPath expression. We only support * Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key * JSONPath queries that trivially convertible to JSON Pointer queries: key
@@ -141,6 +154,15 @@ public:
*/ */
inline simdjson_result<element> at(size_t index) const noexcept; inline simdjson_result<element> at(size_t index) const noexcept;
/**
* Gets the values of items in an array element
* This function has linear-time complexity: the values are checked one by one.
*
* @return The child elements of an array
*/
inline std::vector<element>& get_values(std::vector<element>& out) const noexcept;
/** /**
* Implicitly convert object to element * Implicitly convert object to element
*/ */
@@ -167,8 +189,11 @@ public:
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept; inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
inline void process_json_path_of_child_elements(std::vector<dom::element>::iterator& current, std::vector<dom::element>::iterator& end, const std::string_view& path_suffix, std::vector<dom::element>& accumulator) const noexcept;
inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept; inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at(size_t index) const noexcept; inline simdjson_result<dom::element> at(size_t index) const noexcept;
inline std::vector<dom::element>& get_values(std::vector<dom::element>& out) const noexcept;
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
inline dom::array::iterator begin() const noexcept(false); inline dom::array::iterator begin() const noexcept(false);
+20
View File
@@ -128,6 +128,12 @@ simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_
if (json_pointer == "-1") { return INVALID_JSON_POINTER; } if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer); return at_pointer(json_pointer);
} }
simdjson_inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::element>::at_path_with_wildcard(const std::string_view json_path) const noexcept {
if (error()) { return error(); }
return first.at_path_with_wildcard(json_path);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API #ifndef SIMDJSON_DISABLE_DEPRECATED_API
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at(const std::string_view json_pointer) const noexcept { simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at(const std::string_view json_pointer) const noexcept {
@@ -418,6 +424,20 @@ inline simdjson_result<element> element::at_pointer(std::string_view json_pointe
} }
} }
} }
inline simdjson_result<std::vector<element>> element::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_OBJECT:
return object(tape).at_path_with_wildcard(json_path);
case internal::tape_type::START_ARRAY:
return array(tape).at_path_with_wildcard(json_path);
default:
return std::vector<element>{};
}
}
inline simdjson_result<element> element::at_path(std::string_view json_path) const noexcept { inline simdjson_result<element> element::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path); auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; } if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
+5
View File
@@ -1,6 +1,8 @@
#ifndef SIMDJSON_DOM_ELEMENT_H #ifndef SIMDJSON_DOM_ELEMENT_H
#define SIMDJSON_DOM_ELEMENT_H #define SIMDJSON_DOM_ELEMENT_H
#include <vector>
#include "simdjson/dom/base.h" #include "simdjson/dom/base.h"
#include "simdjson/dom/array.h" #include "simdjson/dom/array.h"
@@ -399,6 +401,8 @@ public:
*/ */
inline simdjson_result<element> at_pointer(const std::string_view json_pointer) const noexcept; inline simdjson_result<element> at_pointer(const std::string_view json_pointer) const noexcept;
inline simdjson_result<std::vector<element>> at_path_with_wildcard(const std::string_view json_path) const noexcept;
/** /**
* Get the value associated with the given JSONPath expression. We only support * Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key * JSONPath queries that trivially convertible to JSON Pointer queries: key
@@ -544,6 +548,7 @@ public:
simdjson_inline simdjson_result<dom::element> operator[](const char *key) const noexcept; simdjson_inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
simdjson_result<dom::element> operator[](int) const noexcept = delete; simdjson_result<dom::element> operator[](int) const noexcept = delete;
simdjson_inline simdjson_result<dom::element> at_pointer(const std::string_view json_pointer) const noexcept; simdjson_inline simdjson_result<dom::element> at_pointer(const std::string_view json_pointer) const noexcept;
simdjson_inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(const std::string_view json_path) const noexcept;
simdjson_inline simdjson_result<dom::element> at_path(const std::string_view json_path) const noexcept; simdjson_inline simdjson_result<dom::element> at_path(const std::string_view json_path) const noexcept;
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
simdjson_inline simdjson_result<dom::element> at(const std::string_view json_pointer) const noexcept; simdjson_inline simdjson_result<dom::element> at(const std::string_view json_pointer) const noexcept;
+112
View File
@@ -40,10 +40,19 @@ inline simdjson_result<dom::element> simdjson_result<dom::object>::at_path(std::
if (json_pointer == "-1") { return INVALID_JSON_POINTER; } if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer); return at_pointer(json_pointer);
} }
inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::object>::at_path_with_wildcard(std::string_view json_path) const noexcept {
if (error()) {
return error();
}
return first.at_path_with_wildcard(json_path);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::string_view key) const noexcept { inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::string_view key) const noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.at_key(key); return first.at_key(key);
} }
inline std::vector<dom::element>& simdjson_result<dom::object>::get_values(std::vector<dom::element>& out) const noexcept {
return first.get_values(out);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key_case_insensitive(std::string_view key) const noexcept { inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key_case_insensitive(std::string_view key) const noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.at_key_case_insensitive(key); return first.at_key_case_insensitive(key);
@@ -143,6 +152,97 @@ inline simdjson_result<element> object::at_path(std::string_view json_path) cons
return at_pointer(json_pointer); return at_pointer(json_pointer);
} }
inline void object::process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept {
if (current == end) {
return;
}
simdjson_result<std::vector<element>> result;
for (auto it = current; it != end; ++it) {
result = it->at_path_with_wildcard(path_suffix);
if (!result.error()) {
std::vector<element> child_result = result.value();
accumulator.reserve(accumulator.size() + child_result.size());
accumulator.insert(accumulator.end(),
std::make_move_iterator(child_result.begin()),
std::make_move_iterator(child_result.end()));
}
}
}
inline simdjson_result<std::vector<element>> object::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i = 0;
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
// if JSONPath starts with $, skip it
// json_path.starts_with('$') requires C++20.
if (json_path.front() == '$') {
i = 1;
}
if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) {
// expect json path to always start with $ but this isn't currently
// expected in jsonpathutil.h.
return INVALID_JSON_POINTER;
}
if (json_path.find("*") != std::string::npos) {
std::vector<element> child_values;
if (
(json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) ||
(json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2)
) {
get_values(child_values);
return child_values;
}
std::pair<std::string_view, std::string_view> key_and_json_path = get_next_key_and_json_path(json_path);
std::string_view key = key_and_json_path.first;
json_path = key_and_json_path.second;
if (key.size() > 0) {
if (key == "*") {
get_values(child_values);
} else {
auto pointer_result = at_pointer("/" + std::string(key));
if (!pointer_result.error()) {
child_values.emplace_back(pointer_result.value());
}
}
std::vector<element> result = {};
if (child_values.size() > 0) {
std::vector<element>::iterator child_values_begin = child_values.begin();
std::vector<element>::iterator child_values_end = child_values.end();
process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result);
}
return result;
} else {
return INVALID_JSON_POINTER;
}
} else {
auto at_path_result = this->at_path(json_path);
if (at_path_result.error()) {
return at_path_result.error();
}
std::vector<element> result{std::move(at_path_result.value())};
return result;
}
}
inline simdjson_result<element> object::at_key(std::string_view key) const noexcept { inline simdjson_result<element> object::at_key(std::string_view key) const noexcept {
iterator end_field = end(); iterator end_field = end();
for (iterator field = begin(); field != end_field; ++field) { for (iterator field = begin(); field != end_field; ++field) {
@@ -152,6 +252,18 @@ inline simdjson_result<element> object::at_key(std::string_view key) const noexc
} }
return NO_SUCH_FIELD; return NO_SUCH_FIELD;
} }
inline std::vector<element>& object::get_values(std::vector<element>& out) const noexcept {
iterator end_field = end();
iterator begin_field = begin();
out.reserve(std::distance(begin_field, end_field));
for (iterator field = begin_field; field != end_field; ++field) {
out.emplace_back(field.value());
}
return out;
}
// In case you wonder why we need this, please see // In case you wonder why we need this, please see
// https://github.com/simdjson/simdjson/issues/323 // https://github.com/simdjson/simdjson/issues/323
// People do seek keys in a case-insensitive manner. // People do seek keys in a case-insensitive manner.
+23
View File
@@ -1,6 +1,8 @@
#ifndef SIMDJSON_DOM_OBJECT_H #ifndef SIMDJSON_DOM_OBJECT_H
#define SIMDJSON_DOM_OBJECT_H #define SIMDJSON_DOM_OBJECT_H
#include <vector>
#include "simdjson/dom/base.h" #include "simdjson/dom/base.h"
#include "simdjson/dom/element.h" #include "simdjson/dom/element.h"
#include "simdjson/internal/tape_ref.h" #include "simdjson/internal/tape_ref.h"
@@ -172,6 +174,16 @@ public:
*/ */
inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept; inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept;
/**
* Recursive function which processes the json path of each child element
*/
inline void process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept;
/**
* Adds support for JSONPath expression with wildcards '*'
*/
inline simdjson_result<std::vector<element>> at_path_with_wildcard(std::string_view json_path) const noexcept;
/** /**
* Get the value associated with the given JSONPath expression. We only support * Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key * JSONPath queries that trivially convertible to JSON Pointer queries: key
@@ -203,6 +215,14 @@ public:
*/ */
inline simdjson_result<element> at_key(std::string_view key) const noexcept; inline simdjson_result<element> at_key(std::string_view key) const noexcept;
/**
* Gets the values associated with keys of an object
* This function has linear-time complexity: the keys are checked one by one.
*
* @return the values associated with each key of an object
*/
inline std::vector<element>& get_values(std::vector<element>& out) const noexcept;
/** /**
* Get the value associated with the given key in a case-insensitive manner. * Get the value associated with the given key in a case-insensitive manner.
* It is only guaranteed to work over ASCII inputs. * It is only guaranteed to work over ASCII inputs.
@@ -261,8 +281,11 @@ public:
inline simdjson_result<dom::element> operator[](const char *key) const noexcept; inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
simdjson_result<dom::element> operator[](int) const noexcept = delete; simdjson_result<dom::element> operator[](int) const noexcept = delete;
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept; inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
inline void process_json_path_of_child_elements(std::vector<dom::element>::iterator& current, std::vector<dom::element>::iterator& end, const std::string_view& path_suffix, std::vector<dom::element>& accumulator) const noexcept;
inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(std::string_view json_path_new) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept; inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_key(std::string_view key) const noexcept; inline simdjson_result<dom::element> at_key(std::string_view key) const noexcept;
inline std::vector<dom::element>& get_values(std::vector<dom::element>& out) const noexcept;
inline simdjson_result<dom::element> at_key_case_insensitive(std::string_view key) const noexcept; inline simdjson_result<dom::element> at_key_case_insensitive(std::string_view key) const noexcept;
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
+48 -3
View File
@@ -4,6 +4,8 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <utility>
namespace simdjson { namespace simdjson {
/** /**
* Converts JSONPath to JSON Pointer. * Converts JSONPath to JSON Pointer.
@@ -12,12 +14,12 @@ namespace simdjson {
*/ */
inline std::string json_path_to_pointer_conversion(std::string_view json_path) { inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
size_t i = 0; size_t i = 0;
// if JSONPath starts with $, skip it // if JSONPath starts with $, skip it
// json_path.starts_with('$') requires C++20.
if (!json_path.empty() && json_path.front() == '$') { if (!json_path.empty() && json_path.front() == '$') {
i = 1; i = 1;
} }
if (json_path.empty() || (json_path[i] != '.' && if (i >= json_path.size() || (json_path[i] != '.' &&
json_path[i] != '[')) { json_path[i] != '[')) {
return "-1"; // This is just a sentinel value, the caller should check for this and return an error. return "-1"; // This is just a sentinel value, the caller should check for this and return an error.
} }
@@ -60,5 +62,48 @@ inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
return result; return result;
} }
inline std::pair<std::string_view, std::string_view> get_next_key_and_json_path(std::string_view& json_path) {
std::string_view key;
if (json_path.empty()) {
return {key, json_path};
}
size_t i = 0;
// if JSONPath starts with $, skip it
if (json_path.front() == '$') {
i = 1;
}
if (i < json_path.length() && json_path[i] == '.') {
i += 1;
size_t key_start = i;
while (i < json_path.length() && json_path[i] != '[' && json_path[i] != '.') {
++i;
}
key = json_path.substr(key_start, i - key_start);
} else if ((i+1 < json_path.size()) && json_path[i] == '[' && (json_path[i+1] == '\'' || json_path[i+1] == '"')) {
i += 2;
size_t key_start = i;
while (i < json_path.length() && json_path[i] != '\'' && json_path[i] != '"') {
++i;
}
key = json_path.substr(key_start, i - key_start);
i += 2;
} else if ((i+2 < json_path.size()) && json_path[i] == '[' && json_path[i+1] == '*' && json_path[i+2] == ']') { // i.e [*].additional_keys or [*]["additional_keys"]
key = "*";
i += 3;
}
return std::make_pair(key, json_path.substr(i));
}
} // namespace simdjson } // namespace simdjson
#endif // SIMDJSON_JSONPATHUTIL_H #endif // SIMDJSON_JSONPATHUTIL_H
+12 -12
View File
@@ -166,22 +166,22 @@ inline namespace literals {
inline namespace string_view_literals { inline namespace string_view_literals {
constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) constexpr std::string_view operator ""_sv( const char* str, size_t len ) noexcept // (1)
{ {
return std::string_view{ str, len }; return std::string_view{ str, len };
} }
constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) constexpr std::u16string_view operator ""_sv( const char16_t* str, size_t len ) noexcept // (2)
{ {
return std::u16string_view{ str, len }; return std::u16string_view{ str, len };
} }
constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) constexpr std::u32string_view operator ""_sv( const char32_t* str, size_t len ) noexcept // (3)
{ {
return std::u32string_view{ str, len }; return std::u32string_view{ str, len };
} }
constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) constexpr std::wstring_view operator ""_sv( const wchar_t* str, size_t len ) noexcept // (4)
{ {
return std::wstring_view{ str, len }; return std::wstring_view{ str, len };
} }
@@ -1512,22 +1512,22 @@ nssv_inline_ns namespace string_view_literals {
#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) nssv_constexpr nonstd::sv_lite::string_view operator ""sv( const char* str, size_t len ) nssv_noexcept // (1)
{ {
return nonstd::sv_lite::string_view{ str, len }; return nonstd::sv_lite::string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) nssv_constexpr nonstd::sv_lite::u16string_view operator ""sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{ {
return nonstd::sv_lite::u16string_view{ str, len }; return nonstd::sv_lite::u16string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) nssv_constexpr nonstd::sv_lite::u32string_view operator ""sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{ {
return nonstd::sv_lite::u32string_view{ str, len }; return nonstd::sv_lite::u32string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) nssv_constexpr nonstd::sv_lite::wstring_view operator ""sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{ {
return nonstd::sv_lite::wstring_view{ str, len }; return nonstd::sv_lite::wstring_view{ str, len };
} }
@@ -1536,22 +1536,22 @@ nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str,
#if nssv_CONFIG_USR_SV_OPERATOR #if nssv_CONFIG_USR_SV_OPERATOR
nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) nssv_constexpr nonstd::sv_lite::string_view operator ""_sv( const char* str, size_t len ) nssv_noexcept // (1)
{ {
return nonstd::sv_lite::string_view{ str, len }; return nonstd::sv_lite::string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) nssv_constexpr nonstd::sv_lite::u16string_view operator ""_sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{ {
return nonstd::sv_lite::u16string_view{ str, len }; return nonstd::sv_lite::u16string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) nssv_constexpr nonstd::sv_lite::u32string_view operator ""_sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{ {
return nonstd::sv_lite::u32string_view{ str, len }; return nonstd::sv_lite::u32string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) nssv_constexpr nonstd::sv_lite::wstring_view operator ""_sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{ {
return nonstd::sv_lite::wstring_view{ str, len }; return nonstd::sv_lite::wstring_view{ str, len };
} }
+12 -12
View File
@@ -776,22 +776,22 @@ inline namespace literals {
inline namespace string_view_literals { inline namespace string_view_literals {
constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) constexpr std::string_view operator ""_sv( const char* str, size_t len ) noexcept // (1)
{ {
return std::string_view{ str, len }; return std::string_view{ str, len };
} }
constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) constexpr std::u16string_view operator ""_sv( const char16_t* str, size_t len ) noexcept // (2)
{ {
return std::u16string_view{ str, len }; return std::u16string_view{ str, len };
} }
constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) constexpr std::u32string_view operator ""_sv( const char32_t* str, size_t len ) noexcept // (3)
{ {
return std::u32string_view{ str, len }; return std::u32string_view{ str, len };
} }
constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) constexpr std::wstring_view operator ""_sv( const wchar_t* str, size_t len ) noexcept // (4)
{ {
return std::wstring_view{ str, len }; return std::wstring_view{ str, len };
} }
@@ -2122,22 +2122,22 @@ nssv_inline_ns namespace string_view_literals {
#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) nssv_constexpr nonstd::sv_lite::string_view operator ""sv( const char* str, size_t len ) nssv_noexcept // (1)
{ {
return nonstd::sv_lite::string_view{ str, len }; return nonstd::sv_lite::string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) nssv_constexpr nonstd::sv_lite::u16string_view operator ""sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{ {
return nonstd::sv_lite::u16string_view{ str, len }; return nonstd::sv_lite::u16string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) nssv_constexpr nonstd::sv_lite::u32string_view operator ""sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{ {
return nonstd::sv_lite::u32string_view{ str, len }; return nonstd::sv_lite::u32string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) nssv_constexpr nonstd::sv_lite::wstring_view operator ""sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{ {
return nonstd::sv_lite::wstring_view{ str, len }; return nonstd::sv_lite::wstring_view{ str, len };
} }
@@ -2146,22 +2146,22 @@ nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str,
#if nssv_CONFIG_USR_SV_OPERATOR #if nssv_CONFIG_USR_SV_OPERATOR
nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) nssv_constexpr nonstd::sv_lite::string_view operator ""_sv( const char* str, size_t len ) nssv_noexcept // (1)
{ {
return nonstd::sv_lite::string_view{ str, len }; return nonstd::sv_lite::string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) nssv_constexpr nonstd::sv_lite::u16string_view operator ""_sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{ {
return nonstd::sv_lite::u16string_view{ str, len }; return nonstd::sv_lite::u16string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) nssv_constexpr nonstd::sv_lite::u32string_view operator ""_sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{ {
return nonstd::sv_lite::u32string_view{ str, len }; return nonstd::sv_lite::u32string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) nssv_constexpr nonstd::sv_lite::wstring_view operator ""_sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{ {
return nonstd::sv_lite::wstring_view{ str, len }; return nonstd::sv_lite::wstring_view{ str, len };
} }
+14 -13
View File
@@ -796,22 +796,22 @@ inline namespace literals {
inline namespace string_view_literals { inline namespace string_view_literals {
constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) constexpr std::string_view operator ""_sv( const char* str, size_t len ) noexcept // (1)
{ {
return std::string_view{ str, len }; return std::string_view{ str, len };
} }
constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) constexpr std::u16string_view operator ""_sv( const char16_t* str, size_t len ) noexcept // (2)
{ {
return std::u16string_view{ str, len }; return std::u16string_view{ str, len };
} }
constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) constexpr std::u32string_view operator ""_sv( const char32_t* str, size_t len ) noexcept // (3)
{ {
return std::u32string_view{ str, len }; return std::u32string_view{ str, len };
} }
constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) constexpr std::wstring_view operator ""_sv( const wchar_t* str, size_t len ) noexcept // (4)
{ {
return std::wstring_view{ str, len }; return std::wstring_view{ str, len };
} }
@@ -2142,22 +2142,22 @@ nssv_inline_ns namespace string_view_literals {
#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) nssv_constexpr nonstd::sv_lite::string_view operator ""sv( const char* str, size_t len ) nssv_noexcept // (1)
{ {
return nonstd::sv_lite::string_view{ str, len }; return nonstd::sv_lite::string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) nssv_constexpr nonstd::sv_lite::u16string_view operator ""sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{ {
return nonstd::sv_lite::u16string_view{ str, len }; return nonstd::sv_lite::u16string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) nssv_constexpr nonstd::sv_lite::u32string_view operator ""sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{ {
return nonstd::sv_lite::u32string_view{ str, len }; return nonstd::sv_lite::u32string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) nssv_constexpr nonstd::sv_lite::wstring_view operator ""sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{ {
return nonstd::sv_lite::wstring_view{ str, len }; return nonstd::sv_lite::wstring_view{ str, len };
} }
@@ -2166,22 +2166,22 @@ nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str,
#if nssv_CONFIG_USR_SV_OPERATOR #if nssv_CONFIG_USR_SV_OPERATOR
nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) nssv_constexpr nonstd::sv_lite::string_view operator ""_sv( const char* str, size_t len ) nssv_noexcept // (1)
{ {
return nonstd::sv_lite::string_view{ str, len }; return nonstd::sv_lite::string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) nssv_constexpr nonstd::sv_lite::u16string_view operator ""_sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{ {
return nonstd::sv_lite::u16string_view{ str, len }; return nonstd::sv_lite::u16string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) nssv_constexpr nonstd::sv_lite::u32string_view operator ""_sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{ {
return nonstd::sv_lite::u32string_view{ str, len }; return nonstd::sv_lite::u32string_view{ str, len };
} }
nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) nssv_constexpr nonstd::sv_lite::wstring_view operator ""_sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{ {
return nonstd::sv_lite::wstring_view{ str, len }; return nonstd::sv_lite::wstring_view{ str, len };
} }
@@ -6826,10 +6826,11 @@ inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
size_t i = 0; size_t i = 0;
// if JSONPath starts with $, skip it // if JSONPath starts with $, skip it
// json_path.starts_with('$') requires C++20.
if (!json_path.empty() && json_path.front() == '$') { if (!json_path.empty() && json_path.front() == '$') {
i = 1; i = 1;
} }
if (json_path.empty() || (json_path[i] != '.' && if (i >= json_path.size() || (json_path[i] != '.' &&
json_path[i] != '[')) { json_path[i] != '[')) {
return "-1"; // This is just a sentinel value, the caller should check for this and return an error. return "-1"; // This is just a sentinel value, the caller should check for this and return an error.
} }
+152 -1
View File
@@ -292,6 +292,157 @@ bool json_path_invalidation() {
} }
TEST_SUCCEED(); TEST_SUCCEED();
} }
bool json_path_with_wildcard() {
TEST_START();
simdjson::padded_string json_string = R"(
{
"firstName": "John",
"lastName" : "doe",
"age" : 26,
"address" : {
"streetAddress": "naist street",
"city" : "Nara",
"postalCode" : "630-0192"
},
"phoneNumbers": [
{
"type" : "iPhone",
"numbers": [
"0123-4567-8888",
"0123-4567-8788",
"0123-4567-8887"
]
},
{
"type" : "home",
"numbers": [
"0123-4567-8910",
"0123-4267-8910",
"0103-4567-8910"
]
},
{ },
{
"type": "office",
"numbers": [ ]
}
],
"empty_object": { },
"empty_array": [ ]
})"_padded;
dom::parser parser;
dom::element parsed_json = parser.parse(json_string);
std::vector<dom::element> values;
std::string_view string_value;
std::uint64_t num_value;
dom::object obj;
ASSERT_EQUAL(parsed_json.at_path_with_wildcard("$").error(), INVALID_JSON_POINTER);
ASSERT_EQUAL(parsed_json.at_path_with_wildcard("1").error(), INVALID_JSON_POINTER);
ASSERT_EQUAL(parsed_json.at_path_with_wildcard("2").error(), INVALID_JSON_POINTER);
ASSERT_EQUAL(parsed_json.at_path_with_wildcard("a").error(), INVALID_JSON_POINTER);
ASSERT_EQUAL(parsed_json.at_path_with_wildcard("$2").error(), INVALID_JSON_POINTER);
ASSERT_EQUAL(parsed_json.at_path_with_wildcard("$a").error(), INVALID_JSON_POINTER);
// $.*
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$.*").get(values));
ASSERT_SUCCESS(values[0].get(string_value));
ASSERT_EQUAL(string_value, "John");
ASSERT_SUCCESS(values[1].get(string_value));
ASSERT_EQUAL(string_value, "doe");
ASSERT_SUCCESS(values[2].get(num_value));
ASSERT_EQUAL(num_value, 26);
ASSERT_SUCCESS(values[3].get(obj));
ASSERT_SUCCESS(obj["streetAddress"].get(string_value));
ASSERT_EQUAL(string_value, "naist street");
ASSERT_SUCCESS(obj["city"].get(string_value));
ASSERT_EQUAL(string_value, "Nara");
// $[*]
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$[*]").get(values));
ASSERT_SUCCESS(values[0].get(string_value));
ASSERT_EQUAL(string_value, "John");
ASSERT_SUCCESS(values[1].get(string_value));
ASSERT_EQUAL(string_value, "doe");
ASSERT_SUCCESS(values[2].get(num_value));
ASSERT_EQUAL(num_value, 26);
ASSERT_SUCCESS(values[3].get(obj));
ASSERT_SUCCESS(obj["streetAddress"].get(string_value));
ASSERT_EQUAL(string_value, "naist street");
ASSERT_SUCCESS(obj["city"].get(string_value));
ASSERT_EQUAL(string_value, "Nara");
// $.address.*
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$.address.*").get(values));
std::vector<std::string> expected = {"naist street", "Nara", "630-0192"};
for (int i = 0; i < 3; i++) {
ASSERT_SUCCESS(values[i].get(string_value));
ASSERT_EQUAL(string_value, expected[i]);
}
// $.*.streetAddress
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$.*.streetAddress").get(values));
ASSERT_SUCCESS(values[0].get(string_value));
ASSERT_EQUAL(string_value, "naist street");
// $.phoneNumbers[*].numbers[*]
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$.phoneNumbers[*].numbers[*]").get(values));
std::vector<std::string> expected_numbers = {
"0123-4567-8888",
"0123-4567-8788",
"0123-4567-8887",
"0123-4567-8910",
"0123-4267-8910",
"0103-4567-8910"
};
for (int i = 0; i < 6; i++) {
ASSERT_SUCCESS(values[i].get(string_value));
ASSERT_EQUAL(string_value, expected_numbers[i]);
}
// $.phoneNumbers[*].numbers[1]
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$.phoneNumbers[*].numbers[1]").get(values));
ASSERT_SUCCESS(values[0].get(string_value));
ASSERT_EQUAL(string_value, expected_numbers[1]);
ASSERT_SUCCESS(values[1].get(string_value));
ASSERT_EQUAL(string_value, expected_numbers[4]);
// $.empty_object.*
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$.empty_object.*").get(values));
ASSERT_EQUAL(values.size(), 0);
// $.empty_array.*
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$.empty_array.*").get(values));
ASSERT_EQUAL(values.size(), 0);
// $.phoneNumbers.*.numbers[3]
ASSERT_SUCCESS(parsed_json.at_path_with_wildcard("$.phoneNumbers.*.numbers[3]").get(values));
ASSERT_EQUAL(values.size(), 0);
TEST_SUCCEED();
}
// for 0.5 version and following (standard compliant) // for 0.5 version and following (standard compliant)
bool modern_support() { bool modern_support() {
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
@@ -314,7 +465,7 @@ bool modern_support() {
} }
int main() { int main() {
if (true && demo() && modern_support() && if (true && json_path_with_wildcard() && demo() && modern_support() &&
run_success_test(TEST_RFC_JSON, "$.foo", "[\"bar\",\"baz\"]") && run_success_test(TEST_RFC_JSON, "$.foo", "[\"bar\",\"baz\"]") &&
run_success_test(TEST_RFC_JSON, "$.foo[0]", "\"bar\"") && run_success_test(TEST_RFC_JSON, "$.foo[0]", "\"bar\"") &&
run_success_test(TEST_RFC_JSON, "$.", "0") && run_success_test(TEST_RFC_JSON, "$.", "0") &&
+56
View File
@@ -21,6 +21,62 @@ void basics_2() {
cout << doc; cout << doc;
} }
void wild() {
simdjson::padded_string json_string = R"(
{
"firstName": "John",
"lastName": "doe",
"age": 26,
"address": {
"streetAddress": "naist street",
"city": "Nara",
"postalCode": "630-0192"
},
"phoneNumbers": [
{
"type": "iPhone",
"numbers": ["0123-4567-8888", "0123-4567-8788"]
},
{
"type": "home",
"numbers": ["0123-4567-8910"]
}
]
})"_padded;
dom::parser parser;
dom::element parsed_json = parser.parse(json_string);
std::vector<dom::element> values;
// Fetch all fields in the address object
auto error = parsed_json.at_path_with_wildcard("$.address.*").get(values);
if(error) {
// do something
}
for (auto &value : values) {
std::string_view field;
error = value.get(field);
if(error) {
// do something
}
std::cout << field << std::endl;
}
// Fetch all phone numbers
error = parsed_json.at_path_with_wildcard("$.phoneNumbers[*].numbers[*]").get(values);
if(error) {
// do something
}
for (auto &value : values) {
std::string_view number;
error = value.get(number);
if(error) {
// do something
}
std::cout << number << std::endl;
}
}
void basics_dom_1() { void basics_dom_1() {
auto cars_json = R"( [ auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] }, { "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },