Skip to content

Commit 0c13086

Browse files
committed
add changes
1 parent 891507e commit 0c13086

File tree

11 files changed

+288
-306
lines changed

11 files changed

+288
-306
lines changed

README.md

Lines changed: 98 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -9,171 +9,17 @@ Inspired by XPath for XML structures, JsonPath is a query language for JSON.
99
The specification is described in [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html).
1010

1111
# Important note
12-
The version 1.0.0 is a breaking change. The library has been rewritten from scratch to provide compliance with the RFC 9535.
12+
The version 1.0.0 has a breaking change. The library has been rewritten from scratch to provide compliance with the RFC 9535.
1313
The changes are:
1414
- The library is now fully compliant with the RFC 9535.
1515
- The api and structures have been changed completely.
1616
- The functions in, nin, noneOf, anyOf, subsetOf are now implemented as custom filter expressions. (TBD)
17+
- The function length was removed (the size can be checked using rust native functions).
1718

1819
## The compliance with RFC 9535
1920

2021
The library is fully compliant with the standard [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html)
21-
22-
## Examples
23-
24-
The following json is given:
25-
26-
```json
27-
{
28-
"shop": {
29-
"orders": [
30-
{
31-
"id": 1,
32-
"active": true
33-
},
34-
{
35-
"id": 2
36-
},
37-
{
38-
"id": 3
39-
},
40-
{
41-
"id": 4,
42-
"active": true
43-
}
44-
]
45-
}
46-
}
47-
```
48-
The following query is given (find all orders id that have the field 'active'):
49-
- `$.shop.orders[[email protected]].id`
50-
51-
The result is `[1,4]`
52-
53-
## The jsonpath description
54-
55-
### Functions
56-
57-
#### Size
58-
59-
A function `length()` transforms the output of the filtered expression into a size of this element
60-
It works with arrays, therefore it returns a length of a given array, otherwise null.
61-
62-
`$.some_field.length()`
63-
64-
**To use it** for objects, the operator `[*]` can be used.
65-
`$.object.[*].length()`
66-
67-
### Operators
68-
69-
| Operator | Description | Where to use |
70-
|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
71-
| `$` | Pointer to the root of the json. | It is gently advising to start every jsonpath from the root. Also, inside the filters to point out that the path is starting from the root. |
72-
| `@` | Pointer to the current element inside the filter operations. | It is used inside the filter operations to iterate the collection. |
73-
| `*` or `[*]` | Wildcard. It brings to the list all objects and elements regardless their names. | It is analogue a flatmap operation. |
74-
| `<..>` | Descent operation. It brings to the list all objects, children of that objects and etc | It is analogue a flatmap operation. |
75-
| `.<name>` or `.['<name>']` | the key pointing to the field of the object | It is used to obtain the specific field. |
76-
| `['<name>' (, '<name>')]` | the list of keys | the same usage as for a single key but for list |
77-
| `[<number>]` | the filter getting the element by its index. | |
78-
| `[<number> (, <number>)]` | the list if elements of array according to their indexes representing these numbers. | |
79-
| `[<start>:<end>:<step>]` | slice operator to get a list of element operating with their indexes. By default step = 1, start = 0, end = array len. The elements can be omitted ```[::]``` | |
80-
| `[?<expression>]` | the logical expression to filter elements in the list. | It is used with arrays preliminary. |
81-
82-
### Filter expressions
83-
84-
Filter expressions are used to filter the elements in the list or values in the object.
85-
86-
The expressions appear in the filter operator like that `[[email protected] > 0]`. The expression in general consists of the
87-
following elements:
88-
89-
- Left and right operands, that is ,in turn, can be a static value,representing as a primitive type like a number,
90-
string value `'value'`, array of them or another json path instance.
91-
- Expression sign, denoting what action can be performed
92-
93-
| Expression sign | Description | Where to use |
94-
|-----------------|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
95-
| `!` | Not | To negate the expression |
96-
| `==` | Equal | To compare numbers or string literals |
97-
| `!=` | Unequal | To compare numbers or string literals in opposite way to equals |
98-
| `<` | Less | To compare numbers |
99-
| `>` | Greater | To compare numbers |
100-
| `<=` | Less or equal | To compare numbers |
101-
| `>=` | Greater or equal | To compare numbers |
102-
| `~=` | Regular expression | To find the incoming right side in the left side. |
103-
| `in` | Find left element in the list of right elements. | |
104-
| `nin` | The same one as saying above but carrying the opposite sense. | |
105-
| `size` | The size of array on the left size should be corresponded to the number on the right side. | |
106-
| `noneOf` | The left size has no intersection with right | |
107-
| `anyOf` | The left size has at least one intersection with right | |
108-
| `subsetOf` | The left is a subset of the right side | |
109-
| `?` | Exists operator. | The operator checks the existence of the field depicted on the left side like that `[[email protected]]` |
110-
111-
Filter expressions can be chained using `||` and `&&` (logical or and logical and correspondingly) in the following way:
112-
113-
```json
114-
{
115-
"key": [
116-
{
117-
"city": "London",
118-
"capital": true,
119-
"size": "big"
120-
},
121-
{
122-
"city": "Berlin",
123-
"capital": true,
124-
"size": "big"
125-
},
126-
{
127-
"city": "Tokyo",
128-
"capital": true,
129-
"size": "big"
130-
},
131-
{
132-
"city": "Moscow",
133-
"capital": true,
134-
"size": "big"
135-
},
136-
{
137-
"city": "Athlon",
138-
"capital": false,
139-
"size": "small"
140-
},
141-
{
142-
"city": "Dortmund",
143-
"capital": false,
144-
"size": "big"
145-
},
146-
{
147-
"city": "Dublin",
148-
"capital": true,
149-
"size": "small"
150-
}
151-
]
152-
}
153-
```
154-
155-
The path ``` $.key[[email protected] == false || @size == 'small'].city ``` will give the following result:
156-
157-
```json
158-
[
159-
"Athlon",
160-
"Dublin",
161-
"Dortmund"
162-
]
163-
```
164-
165-
And the path ``` $.key[[email protected] == false && @size != 'small'].city ``` ,in its turn, will give the following result:
166-
167-
```json
168-
[
169-
"Dortmund"
170-
]
171-
```
172-
173-
By default, the operators have the different priority so `&&` has a higher priority so to change it the brackets can be
174-
used.
175-
``` $.[[email protected] == 0 || @.f == 1) && ($.x == 15)].city ```
176-
22+
17723
## Examples
17824

17925
Given the json
@@ -239,65 +85,130 @@ Given the json
23985

24086
## Library Usage
24187

242-
243-
24488
### Queryable
24589

24690
The library provides a trait `Queryable` that can be implemented for any type.
24791
This allows you to use the `JsonPath` methods on your own types.
24892

93+
### Queried with path
94+
```rust
95+
96+
fn union() -> Queried<()> {
97+
let json = json!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
98+
99+
// QueryRes is a tuple of (value, path) for references and just value for owned values
100+
let vec: Vec<QueryRes<Value>> = json.query_with_path("$[1,5:7]")?;
101+
assert_eq!(
102+
vec,
103+
vec![
104+
(&json!(1), "$[1]".to_string()).into(),
105+
(&json!(5), "$[5]".to_string()).into(),
106+
(&json!(6), "$[6]".to_string()).into(),
107+
]
108+
);
109+
110+
Ok(())
111+
}
112+
113+
```
114+
115+
### Queried without path
116+
```rust
117+
fn exp_no_error() -> Queried<()> {
118+
let json = json!([
119+
{
120+
"a": 100,
121+
"d": "e"
122+
},
123+
{
124+
"a": 100.1,
125+
"d": "f"
126+
},
127+
{
128+
"a": "100",
129+
"d": "g"
130+
}
131+
]);
132+
133+
let vec: Vec<Cow<Value>> = json.query("$[[email protected]==1E2]")?;
134+
assert_eq!(
135+
vec.iter().map(Cow::as_ref).collect::<Vec<_>>(),
136+
vec![&json!({"a":100, "d":"e"})]
137+
);
138+
139+
Ok(())
140+
}
141+
```
142+
143+
### Queried with only path
144+
```rust
145+
fn filter_data() -> Queried<()> {
146+
let json = json!({
147+
"a": 1,
148+
"b": 2,
149+
"c": 3
150+
});
151+
152+
let vec: Vec<String> = json
153+
.query_only_path("$[?@<3]")?
154+
.into_iter()
155+
.map(Option::unwrap_or_default)
156+
.collect();
157+
158+
assert_eq!(vec, vec!["$.['a']".to_string(), "$.['b']".to_string()]);
159+
160+
Ok(())
161+
}
162+
```
163+
249164
### Update the Queryable structure by path
250165

251166
The library does not provide the functionality to update the json structure in the query itself.
252167
Instead, the library provides the ability to update the json structure by the path.
253168
Thus, the user needs to find a path for the `JsonLike` structure and update it manually.
254169

255-
There are two methods in the `JsonLike` trait:
170+
There are two methods in the `Queryable` trait:
256171

257172
- `reference_mut` - returns a mutable reference to the element by the path
258173
- `reference` - returns a reference to the element by the path
259-
They accept a `JsonPath` instance and return a `Option<&mut Value>` or `Option<&Value>` respectively.
260-
The path is supported with the limited elements namely only the elements with the direct access:
174+
175+
They accept a `JsonPath` instance and return a `Option<&mut Self>` or `Option<&Self>` respectively.
176+
177+
The path is supported with the limited elements namely only the elements with the direct access:
261178
- root
262179
- field
263-
- index
264-
The path can be obtained manually or `find_as_path` method can be used.
180+
- index
265181

266182
```rust
267-
#[test]
268-
fn update_by_path_test() -> Result<(), JsonPathParserError> {
269-
let mut json = json!([
183+
fn update_by_path_test() -> Queried<()> {
184+
let mut json = json!([
270185
{"verb": "RUN","distance":[1]},
271186
{"verb": "TEST"},
272187
{"verb": "DO NOT RUN"}
273188
]);
274189

275-
let path: Box<JsonPath> = Box::from(JsonPath::try_from("$.[[email protected] == 'RUN']")?);
276-
let elem = path
277-
.find_as_path(&json)
278-
.get(0)
279-
.cloned()
280-
.ok_or(JsonPathParserError::InvalidJsonPath("".to_string()))?;
281-
282-
if let Some(v) = json
283-
.reference_mut(elem)?
284-
.and_then(|v| v.as_object_mut())
285-
.and_then(|v| v.get_mut("distance"))
286-
.and_then(|v| v.as_array_mut())
287-
{
288-
v.push(json!(2))
289-
}
190+
let path = json.query_only_path("$.[?(@.verb == 'RUN')]")?;
191+
let elem = path.first().cloned().flatten().unwrap_or_default();
290192

291-
assert_eq!(
292-
json,
293-
json!([
193+
if let Some(v) = json
194+
.reference_mut(elem)
195+
.and_then(|v| v.as_object_mut())
196+
.and_then(|v| v.get_mut("distance"))
197+
.and_then(|v| v.as_array_mut())
198+
{
199+
v.push(json!(2))
200+
}
201+
202+
assert_eq!(
203+
json,
204+
json!([
294205
{"verb": "RUN","distance":[1,2]},
295206
{"verb": "TEST"},
296207
{"verb": "DO NOT RUN"}
297208
])
298-
);
209+
);
299210

300-
Ok(())
211+
Ok(())
301212
}
302213
```
303214

0 commit comments

Comments
 (0)