File: README.md

package info (click to toggle)
rust-jsonpath-rust 0.7-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 324 kB
  • sloc: makefile: 2
file content (293 lines) | stat: -rw-r--r-- 10,431 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# jsonpath-rust

[![Crates.io](https://img.shields.io/crates/v/jsonpath-rust)](https://crates.io/crates/jsonpath-rust)
[![docs.rs](https://img.shields.io/docsrs/jsonpath-rust)](https://docs.rs/jsonpath-rust/latest/jsonpath_rust)
[![Rust CI](https://github.com/besok/jsonpath-rust/actions/workflows/ci.yml/badge.svg)](https://github.com/besok/jsonpath-rust/actions/workflows/ci.yml)

The library provides the extensive functionality to find data sets according to filtering queries.
Inspired by XPath for XML structures, JsonPath is a query language for JSON.
The specification is described in [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html).

# Important note

The version 1.0.0 has a breaking change. The library has been rewritten from scratch to provide compliance with the RFC9535.

The changes are:

- The library is now fully compliant with the RFC 9535.
- New structures and apis were introduced to provide the compliance with the RFC 9535.
  - `Queryable` instead of `JsonLike`
  - `Queried<Queryable>` instead of  `Result<Value, JsonPathParserError>`
  - `JsonPath#{query_with_path, query_only_path, query}` to operate with the `Queryable` structure
  - `JsonPathError` instead of `JsonPathParserError`
  - `QueryRef` to provide the reference to the value and path
- The functions in, nin, noneOf, anyOf, subsetOf are now implemented as custom filter expressions and renamed to `in`,
  `nin`, `none_of`, `any_of`, `subset_of` respectively.
- The function length was removed (the size can be checked using rust native functions for using it in filter there is length expression).

## The compliance with RFC 9535

The library is fully compliant (except several cases) with the standard [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html)
To check the compliance with the standard, please be headed to [rfc9535 subfolder](rfc9535/README.md)


## Examples

Given the json

 ```json
{
  "store": {
    "book": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      {
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  },
  "expensive": 10
}
 ```

| JsonPath                           | Result                                                       |
|------------------------------------|:-------------------------------------------------------------|
| `$.store.book[*].author`           | The authors of all books                                     |
| `$..book[?@.isbn]`                 | All books with an ISBN number                                |
| `$.store.*`                        | All things, both books and bicycles                          |
| `$..author`                        | All authors                                                  |
| `$.store..price`                   | The price of everything                                      |
| `$..book[2]`                       | The third book                                               |
| `$..book[-2]`                      | The second to last book                                      |
| `$..book[0,1]`                     | The first two books                                          |
| `$..book[:2]`                      | All books from index 0 (inclusive) until index 2 (exclusive) |
| `$..book[1:2]`                     | All books from index 1 (inclusive) until index 2 (exclusive) |
| `$..book[-2:]`                     | Last two books                                               |
| `$..book[2:]`                      | Book number two from tail                                    |
| `$.store.book[?@.price < 10]`      | All books in store cheaper than 10                           |
| `$..book[?@.price <= $.expensive]` | All books in store that are not "expensive"                  |
| `$..book[?@.author ~= '(?i)REES']` | All books matching regex (ignore case)                       |
| `$..*`                             | Give me every thing                                          |

## Library Usage

### Extensions
The library provides the following extensions:

- **in**  
Checks if the first argument is in the array provided as the second argument. Example: `$.elems[?in(@, $.list)]` 
Returns elements from `$.elems` that are present in `$.list`.

- **nin**  
Checks if the first argument is not in the array provided as the second argument. Example: `$.elems[?nin(@, $.list)]`
Returns elements from `$.elems` that are not present in `$.list`.

- **none_of**  
Checks if none of the elements in the first array are in the second array. Example: `$.elems[?none_of(@, $.list)]` 
Returns arrays from `$.elems` that have no elements in common with `$.list`.

- **any_of**  
Checks if any of the elements in the first array are in the second array. Example: `$.elems[?any_of(@, $.list)]` 
Returns arrays from `$.elems` that have at least one element in common with `$.list`.

- **subset_of**  
Checks if all elements in the first array are in the second array. Example: `$.elems[?subset_of(@, $.list)]` 
Returns arrays from `$.elems` where all elements are present in `$.list`.


### Queryable

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

### Queried with path

```rust

fn union() -> Queried<()> {
    let json = json!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

    // QueryRes is a tuple of (value, path) for references and just value for owned values
    let vec: Vec<QueryRef<Value>> = json.query_with_path("$[1,5:7]")?;
    assert_eq!(
        vec,
        vec![
            (&json!(1), "$[1]".to_string()).into(),
            (&json!(5), "$[5]".to_string()).into(),
            (&json!(6), "$[6]".to_string()).into(),
        ]
    );

    Ok(())
}

```

### Queried without path

```rust
fn exp_no_error() -> Queried<()> {
    let json = json!([
      {
        "a": 100,
        "d": "e"
      },
      {
        "a": 100.1,
        "d": "f"
      },
      {
        "a": "100",
        "d": "g"
      }
    ]);

    let vec: Vec<&Value> = json.query("$[?@.a==1E2]")?;
    assert_eq!(
        vec.iter().collect::<Vec<_>>(),
        vec![&json!({"a":100, "d":"e"})]
    );

    Ok(())
}
```

### Queried with only path

```rust
fn filter_data() -> Queried<()> {
    let json = json!({
      "a": 1,
      "b": 2,
      "c": 3
    });

    let vec: Vec<String> = json
        .query_only_path("$[?@<3]")?
        .into_iter()
        .map(Option::unwrap_or_default)
        .collect();

    assert_eq!(vec, vec!["$['a']".to_string(), "$['b']".to_string()]);

    Ok(())
}
```

### Update the Queryable structure by path

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

There are two methods in the `Queryable` trait:

- `reference_mut` - returns a mutable reference to the element by the path
- `reference` - returns a reference to the element by the path

They accept a `JsonPath` instance and return a `Option<&mut Self>` or `Option<&Self>` respectively.

The path is supported with the limited elements namely only the elements with the direct access:

- root
- field
- index

```rust
 fn update_by_path_test() -> Queried<()> {
    let mut json = json!([
            {"verb": "RUN","distance":[1]},
            {"verb": "TEST"},
            {"verb": "DO NOT RUN"}
        ]);

    let path = json.query_only_path("$.[?(@.verb == 'RUN')]")?;
    let elem = path.first().unwrap_or_default();

    if let Some(v) = json
        .reference_mut(elem)
        .and_then(|v| v.as_object_mut())
        .and_then(|v| v.get_mut("distance"))
        .and_then(|v| v.as_array_mut())
    {
        v.push(json!(2))
    }

    assert_eq!(
        json,
        json!([
                {"verb": "RUN","distance":[1,2]},
                {"verb": "TEST"},
                {"verb": "DO NOT RUN"}
            ])
    );

    Ok(())
}
```

### Compiled Paths 
🚧Under Construction: Unstable/Unimplemented🚧

By enabling the `compiled-path` feature, the following syntax becomes available:
```rust
fn macros() {
  // Existing
  let vec = js_path("$.values[?match(@, $.regex)]", &json)?;
  // New
  let q_ast: JpQuery = ::jsonpath_rust::json_query!($.values[?match(@, $.regex)]);
}
```

This allows for query strings to be created infallibly at compile time for applications where query strings will be static strings in source code.

#### Limitations Of Compiled Path Queries
- Single quote strings are not allowed, however to replace this, rust's [raw string literals](https://doc.rust-lang.org/rust-by-example/std/str.html) such as `r"# ... #"` can be used.
- The macro does not check whitespace, this means that with respect to whitespace, the domain of strings accepted by the macro is a superset of those accepted by the original RFC.
- Due to  [constraints on rust identifiers](https://internals.rust-lang.org/t/supporting-emoji-in-identifiers/16838), emoji in member name shorthands such as `json_query!( $.☺ )` are not allowed
  - Unicode characters still work in both string literals and bracket field access, ie: `json_query!( $["☺"] )`

### Python bindings

Python bindings ([jsonpath-rust-bindings](https://github.com/night-crawler/jsonpath-rust-bindings)) are available on
pypi:

```bash
pip install jsonpath-rust-bindings
```

## How to contribute

TBD

## How to update version

- update files
- commit them
- add tag `git tag -a v<Version> -m "message"`
- git push origin <tag_name>