Skip to content

Commit 3eb0149

Browse files
authored
cleanup after 0.6.0 changes (#66)
* mention the License via name, so that its shown on https://crates.io/crates/jsonpath-rust instead of "non-standart", update to rust 2021 * provide a minimal example code, fix clippy issues and harden CI to verify everything * update README.md with most current usage. try to keep it simple, and link to docs as they are actually tested in CI via `cargo test`
1 parent 1d375e6 commit 3eb0149

File tree

8 files changed

+101
-169
lines changed

8 files changed

+101
-169
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
profile: minimal
2929
toolchain: stable
3030
components: clippy
31-
- run: cargo clippy --workspace --tests --all-features -- -D warnings
31+
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
3232

3333
test:
3434
runs-on: ubuntu-latest

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name = "jsonpath-rust"
33
description = "The library provides the basic functionality to find the set of the data according to the filtering query."
44
version = "0.6.0"
55
authors = ["BorisZhguchev <[email protected]>"]
6-
edition = "2018"
7-
license-file = "LICENSE"
6+
edition = "2021"
7+
license = "MIT"
88
homepage = "https://github.com/besok/jsonpath-rust"
99
repository = "https://github.com/besok/jsonpath-rust"
1010
readme = "README.md"

README.md

Lines changed: 23 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ Given the json
231231
| `$..book[?(@.author ~= '(?i)REES')]` | All books matching regex (ignore case) |
232232
| `$..*` | Give me every thing |
233233

234-
### The library
234+
## Library Usage
235235

236236
The library intends to provide the basic functionality for ability to find the slices of data using the syntax, saying
237237
above. The dependency can be found as following:
@@ -251,180 +251,43 @@ To extract data there are two methods, provided on the `value`:
251251
```rust
252252
let v:JsonPathValue<Value> =...
253253
v.to_data();
254-
v.slice_or( & some_dafult_value)
255-
254+
v.slice_or(&some_dafault_value)
256255
```
257256

258-
```rust
259-
use jsonpath_rust::JsonPathFinder;
260-
use serde_json::{json, Value, JsonPathValue};
257+
### Find
261258

262-
fn main() {
263-
let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.first.second[?(@.active)]").unwrap();
264-
let slice_of_data: Vec<&Value> = finder.find_slice();
265-
let js = json!({"active":1});
266-
assert_eq!(slice_of_data, vec![JsonPathValue::Slice(&js,"$.first.second[0]".to_string())]);
267-
}
268-
```
259+
there are 3 different functions to find data inside a `value`.
260+
All take references, to increase reusability. Especially json parsing and jsonpath parsing can take significant time, compared to a simple find.
269261

270-
or with a separate instantiation:
262+
The methods `find`, `find_as_path` and `find_slice` take the same inputs, but handle them differently depending on your usecase. They are further described in the [docs](https://docs.rs/jsonpath-rust/latest/jsonpath_rust/index.html#functions).
271263

272264
```rust
273-
use serde_json::{json, Value};
274-
use crate::jsonpath_rust::{JsonPathFinder, JsonPathQuery, JsonPathInst, JsonPathValue};
265+
use jsonpath_rust::{JsonPathInst, JsonPathValue};
266+
use serde_json::json;
275267
use std::str::FromStr;
276268

277-
fn test() {
278-
let json: Value = serde_json::from_str("{}").unwrap();
279-
let v = json.path("$..book[?(@.author size 10)].title").unwrap();
280-
assert_eq!(v, json!([]));
281-
282-
let json: Value = serde_json::from_str("{}").unwrap();
283-
let path = &json.path("$..book[?(@.author size 10)].title").unwrap();
284-
285-
assert_eq!(path, &json!(["Sayings of the Century"]));
286-
287-
let json: Box<Value> = serde_json::from_str("{}").unwrap();
288-
let path: Box<JsonPathInst> = Box::from(JsonPathInst::from_str("$..book[?(@.author size 10)].title").unwrap());
289-
let finder = JsonPathFinder::new(json, path);
290-
291-
let v = finder.find_slice();
292-
let js = json!("Sayings of the Century");
293-
assert_eq!(v, vec![JsonPathValue::Slice(&js,"$.book[0].title".to_string())]);
294-
}
295-
296-
```
297-
In case, if there is no match `find_slice` will return `vec![NoValue]` and `find` return `json!(null)`
298-
299-
```rust
300-
use jsonpath_rust::JsonPathFinder;
301-
use serde_json::{json, Value, JsonPathValue};
302-
303-
fn main() {
304-
let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.no_field").unwrap();
305-
let res_js = finder.find();
306-
assert_eq!(res_js, json!(null));
307-
}
308-
```
309-
310-
also, it will work with the instances of [[Value]] as well.
311-
312-
```rust
313-
use serde_json::Value;
314-
use crate::jsonpath_rust::{JsonPathFinder, JsonPathQuery, JsonPathInst};
315-
use crate::path::{json_path_instance, PathInstance};
316-
317-
fn test(json: Box<Value>, path: &str) {
318-
let path = JsonPathInst::from_str(path).unwrap();
319-
JsonPathFinder::new(json, path)
320-
}
321-
```
322-
323-
also, the trait `JsonPathQuery` can be used:
324-
325-
```rust
326-
327-
use serde_json::{json, Value};
328-
use jsonpath_rust::JsonPathQuery;
329-
330-
fn test() {
331-
let json: Value = serde_json::from_str("{}").unwrap();
332-
let v = json.path("$..book[?(@.author size 10)].title").unwrap();
333-
assert_eq!(v, json!([]));
334-
335-
let json: Value = serde_json::from_str(template_json()).unwrap();
336-
let path = &json.path("$..book[?(@.author size 10)].title").unwrap();
337-
338-
assert_eq!(path, &json!(["Sayings of the Century"]));
339-
}
340-
```
341-
342-
also, `JsonPathInst` can be used to query the data without cloning.
343-
```rust
344-
use serde_json::{json, Value};
345-
use crate::jsonpath_rust::{JsonPathInst};
346-
347-
fn test() {
348-
let json: Value = serde_json::from_str("{}").expect("to get json");
349-
let query = JsonPathInst::from_str("$..book[?(@.author size 10)].title").unwrap();
350-
351-
// To convert to &Value, use deref()
352-
assert_eq!(query.find_slice(&json).get(0).expect("to get value").deref(), &json!("Sayings of the Century"));
353-
}
354-
```
355-
356-
The library can return a path describing the value instead of the value itself.
357-
To do that, the method `find_as_path` can be used:
358-
359-
```rust
360-
use jsonpath_rust::JsonPathFinder;
361-
use serde_json::{json, Value, JsonPathValue};
362-
363269
fn main() {
364-
let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.first.second[?(@.active)]").unwrap();
365-
let slice_of_data: Value = finder.find_as_path();
366-
assert_eq!(slice_of_data, Value::Array(vec!["$.first.second[0]".to_string()]));
367-
}
368-
```
270+
let data = json!({"first":{"second":[{"active":1},{"passive":1}]}});
271+
let path = JsonPathInst::from_str("$.first.second[?(@.active)]").unwrap();
272+
let slice_of_data = jsonpath_rust::find_slice(&path, &data);
369273

370-
or it can be taken from the `JsonPathValue` instance:
371-
```rust
372-
use serde_json::{json, Value};
373-
use crate::jsonpath_rust::{JsonPathFinder, JsonPathQuery, JsonPathInst, JsonPathValue};
374-
use std::str::FromStr;
375-
376-
fn test() {
377-
let json: Box<Value> = serde_json::from_str("{}").unwrap();
378-
let path: Box<JsonPathInst> = Box::from(JsonPathInst::from_str("$..book[?(@.author size 10)].title").unwrap());
379-
let finder = JsonPathFinder::new(json, path);
274+
let expected_value = json!({"active":1});
275+
let expected_path = "$.['first'].['second'][0]".to_string();
380276

381-
let v = finder.find_slice();
382-
let js = json!("Sayings of the Century");
383-
384-
// Slice has a path of its value as well
385-
assert_eq!(v, vec![JsonPathValue::Slice(&js,"$.book[0].title".to_string())]);
277+
assert_eq!(
278+
slice_of_data,
279+
vec![JsonPathValue::Slice(&expected_value, expected_path)]
280+
);
386281
}
387282
```
388283

389-
** If the value has been modified during the search, there is no way to find a path of a new value.
390-
It can happen if we try to find a length() of array, for in stance.**
391-
284+
### The structure
392285

286+
The internal structure of the `JsonPath` can be found here:
287+
https://docs.rs/jsonpath-rust/latest/jsonpath_rust/parser/model/enum.JsonPath.html
393288

394-
## The structure
395-
396-
```rust
397-
pub enum JsonPath {
398-
Root,
399-
// <- $
400-
Field(String),
401-
// <- field of the object
402-
Chain(Vec<JsonPath>),
403-
// <- the whole jsonpath
404-
Descent(String),
405-
// <- '..'
406-
Index(JsonPathIndex),
407-
// <- the set of indexes represented by the next structure [[JsonPathIndex]]
408-
Current(Box<JsonPath>),
409-
// <- @
410-
Wildcard,
411-
// <- *
412-
Empty, // the structure to avoid inconsistency
413-
}
414-
415-
pub enum JsonPathIndex {
416-
Single(usize),
417-
// <- [1]
418-
UnionIndex(Vec<f64>),
419-
// <- [1,2,3]
420-
UnionKeys(Vec<String>),
421-
// <- ['key_1','key_2']
422-
Slice(i32, i32, usize),
423-
// [0:10:1]
424-
Filter(Operand, FilterSign, Operand), // <- [?(operand sign operand)]
425-
}
426-
427-
```
289+
The internal structure of the `JsonPathIndex` can be found here:
290+
https://docs.rs/jsonpath-rust/latest/jsonpath_rust/parser/model/enum.JsonPathIndex.html
428291

429292
## How to contribute
430293

@@ -434,4 +297,4 @@ TBD
434297
- update files
435298
- commit them
436299
- add tag `git tag -a v<Version> -m "message"`
437-
- git push origin <tag_name>
300+
- git push origin <tag_name>

benches/equal.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ struct SearchData {
88
path: JsonPathInst,
99
}
1010

11-
const PATH: &'static str = "$.[?(@.author == 'abcd(Rees)')]";
11+
const PATH: &str = "$.[?(@.author == 'abcd(Rees)')]";
1212

1313
fn equal_perf_test_with_reuse(cfg: &SearchData) {
1414
let _v = jsonpath_rust::find(&cfg.path, &cfg.json);
@@ -33,7 +33,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
3333
b.iter(|| equal_perf_test_with_reuse(&data))
3434
});
3535
c.bench_function("equal bench without reuse", |b| {
36-
b.iter(|| equal_perf_test_without_reuse())
36+
b.iter(equal_perf_test_without_reuse)
3737
});
3838
}
3939

benches/regex.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ struct SearchData {
88
path: JsonPathInst,
99
}
1010

11-
const PATH: &'static str = "$.[?(@.author ~= '.*(?i)d\\(Rees\\)')]";
11+
const PATH: &str = "$.[?(@.author ~= '.*(?i)d\\(Rees\\)')]";
1212

1313
fn regex_perf_test_with_reuse(cfg: &SearchData) {
1414
let _v = jsonpath_rust::find(&cfg.path, &cfg.json);
@@ -37,10 +37,10 @@ pub fn criterion_benchmark(c: &mut Criterion) {
3737
b.iter(|| regex_perf_test_with_reuse(&data))
3838
});
3939
c.bench_function("regex bench without reuse", |b| {
40-
b.iter(|| regex_perf_test_without_reuse())
40+
b.iter(regex_perf_test_without_reuse)
4141
});
4242
c.bench_function("JsonPathInst generation", |b| {
43-
b.iter(|| json_path_inst_compiling())
43+
b.iter(json_path_inst_compiling)
4444
});
4545
}
4646

examples/hello-world.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use jsonpath_rust::JsonPathInst;
2+
use serde_json::json;
3+
use std::str::FromStr;
4+
5+
fn main() {
6+
let data = json!({
7+
"Hello":"World",
8+
"Good":"Bye",
9+
});
10+
let path = JsonPathInst::from_str("$.Hello").unwrap();
11+
let search_result = jsonpath_rust::find(&path, &data);
12+
println!("Hello, {}", search_result);
13+
}

src/lib.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,27 @@ impl<'a, Data> JsonPathValue<'a, Data> {
397397

398398
/// finds a slice of data in the set json.
399399
/// The result is a vector of references to the incoming structure.
400+
///
401+
/// In case, if there is no match `find_slice` will return `vec![NoValue]`.
402+
///
403+
/// ## Example
404+
/// ```rust
405+
/// use jsonpath_rust::{JsonPathInst, JsonPathValue};
406+
/// use serde_json::json;
407+
/// # use std::str::FromStr;
408+
///
409+
/// let data = json!({"first":{"second":[{"active":1},{"passive":1}]}});
410+
/// let path = JsonPathInst::from_str("$.first.second[?(@.active)]").unwrap();
411+
/// let slice_of_data = jsonpath_rust::find_slice(&path, &data);
412+
///
413+
/// let expected_value = json!({"active":1});
414+
/// let expected_path = "$.['first'].['second'][0]".to_string();
415+
///
416+
/// assert_eq!(
417+
/// slice_of_data,
418+
/// vec![JsonPathValue::Slice(&expected_value, expected_path)]
419+
/// );
420+
/// ```
400421
pub fn find_slice<'a>(path: &'a JsonPathInst, json: &'a Value) -> Vec<JsonPathValue<'a, Value>> {
401422
let instance = json_path_instance(&path.inner, json);
402423
let res = instance.find(JsonPathValue::from_root(json));
@@ -411,6 +432,21 @@ pub fn find_slice<'a>(path: &'a JsonPathInst, json: &'a Value) -> Vec<JsonPathVa
411432

412433
/// finds a slice of data and wrap it with Value::Array by cloning the data.
413434
/// Returns either an array of elements or Json::Null if the match is incorrect.
435+
///
436+
/// In case, if there is no match `find` will return `json!(null)`.
437+
///
438+
/// ## Example
439+
/// ```rust
440+
/// use jsonpath_rust::{JsonPathInst, JsonPathValue};
441+
/// use serde_json::{Value, json};
442+
/// # use std::str::FromStr;
443+
///
444+
/// let data = json!({"first":{"second":[{"active":1},{"passive":1}]}});
445+
/// let path = JsonPathInst::from_str("$.first.second[?(@.active)]").unwrap();
446+
/// let cloned_data = jsonpath_rust::find(&path, &data);
447+
///
448+
/// assert_eq!(cloned_data, Value::Array(vec![json!({"active":1})]));
449+
/// ```
414450
pub fn find(path: &JsonPathInst, json: &Value) -> Value {
415451
let slice = find_slice(path, json);
416452
if !slice.is_empty() {
@@ -429,8 +465,26 @@ pub fn find(path: &JsonPathInst, json: &Value) -> Value {
429465
Value::Array(vec![])
430466
}
431467
}
432-
/// finds a path of the values.
468+
469+
/// finds a path describing the value, instead of the value itself.
433470
/// If the values has been obtained by moving the data out of the initial json the path is absent.
471+
///
472+
/// ** If the value has been modified during the search, there is no way to find a path of a new value.
473+
/// It can happen if we try to find a length() of array, for in stance.**
474+
///
475+
/// ## Example
476+
/// ```rust
477+
/// use jsonpath_rust::{JsonPathInst, JsonPathValue};
478+
/// use serde_json::{Value, json};
479+
/// # use std::str::FromStr;
480+
///
481+
/// let data = json!({"first":{"second":[{"active":1},{"passive":1}]}});
482+
/// let path = JsonPathInst::from_str("$.first.second[?(@.active)]").unwrap();
483+
/// let slice_of_data: Value = jsonpath_rust::find_as_path(&path, &data);
484+
///
485+
/// let expected_path = "$.['first'].['second'][0]".to_string();
486+
/// assert_eq!(slice_of_data, Value::Array(vec![Value::String(expected_path)]));
487+
/// ```
434488
pub fn find_as_path(path: &JsonPathInst, json: &Value) -> Value {
435489
Value::Array(
436490
find_slice(path, json)

src/parser/parser.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::empty_docs)]
2+
13
use crate::parser::errors::JsonPathParserError::ParserError;
24
use crate::parser::errors::{parser_err, JsonPathParserError};
35
use crate::parser::model::FilterExpression::{And, Not, Or};

0 commit comments

Comments
 (0)