From 17be45a47445637edb4ea314e351d12529b64c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 10 Jun 2024 08:32:27 +0200 Subject: [PATCH] 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` --- README.md | 183 +++++++---------------------------------------------- src/lib.rs | 56 +++++++++++++++- 2 files changed, 78 insertions(+), 161 deletions(-) diff --git a/README.md b/README.md index 8384f80..a46dbc0 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ Given the json | `$..book[?(@.author ~= '(?i)REES')]` | All books matching regex (ignore case) | | `$..*` | Give me every thing | -### The library +## Library Usage The library intends to provide the basic functionality for ability to find the slices of data using the syntax, saying above. The dependency can be found as following: @@ -251,180 +251,43 @@ To extract data there are two methods, provided on the `value`: ```rust let v:JsonPathValue =... v.to_data(); -v.slice_or( & some_dafult_value) - +v.slice_or(&some_dafault_value) ``` -```rust -use jsonpath_rust::JsonPathFinder; -use serde_json::{json, Value, JsonPathValue}; +### Find -fn main() { - let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.first.second[?(@.active)]").unwrap(); - let slice_of_data: Vec<&Value> = finder.find_slice(); - let js = json!({"active":1}); - assert_eq!(slice_of_data, vec![JsonPathValue::Slice(&js,"$.first.second[0]".to_string())]); -} -``` +there are 3 different functions to find data inside a `value`. +All take references, to increase reusability. Especially json parsing and jsonpath parsing can take significant time, compared to a simple find. -or with a separate instantiation: +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). ```rust -use serde_json::{json, Value}; -use crate::jsonpath_rust::{JsonPathFinder, JsonPathQuery, JsonPathInst, JsonPathValue}; +use jsonpath_rust::{JsonPathInst, JsonPathValue}; +use serde_json::json; use std::str::FromStr; -fn test() { - let json: Value = serde_json::from_str("{}").unwrap(); - let v = json.path("$..book[?(@.author size 10)].title").unwrap(); - assert_eq!(v, json!([])); - - let json: Value = serde_json::from_str("{}").unwrap(); - let path = &json.path("$..book[?(@.author size 10)].title").unwrap(); - - assert_eq!(path, &json!(["Sayings of the Century"])); - - let json: Box = serde_json::from_str("{}").unwrap(); - let path: Box = Box::from(JsonPathInst::from_str("$..book[?(@.author size 10)].title").unwrap()); - let finder = JsonPathFinder::new(json, path); - - let v = finder.find_slice(); - let js = json!("Sayings of the Century"); - assert_eq!(v, vec![JsonPathValue::Slice(&js,"$.book[0].title".to_string())]); -} - -``` -In case, if there is no match `find_slice` will return `vec![NoValue]` and `find` return `json!(null)` - -```rust -use jsonpath_rust::JsonPathFinder; -use serde_json::{json, Value, JsonPathValue}; - -fn main() { - let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.no_field").unwrap(); - let res_js = finder.find(); - assert_eq!(res_js, json!(null)); -} -``` - -also, it will work with the instances of [[Value]] as well. - -```rust - use serde_json::Value; -use crate::jsonpath_rust::{JsonPathFinder, JsonPathQuery, JsonPathInst}; -use crate::path::{json_path_instance, PathInstance}; - -fn test(json: Box, path: &str) { - let path = JsonPathInst::from_str(path).unwrap(); - JsonPathFinder::new(json, path) -} -``` - -also, the trait `JsonPathQuery` can be used: - -```rust - -use serde_json::{json, Value}; -use jsonpath_rust::JsonPathQuery; - -fn test() { - let json: Value = serde_json::from_str("{}").unwrap(); - let v = json.path("$..book[?(@.author size 10)].title").unwrap(); - assert_eq!(v, json!([])); - - let json: Value = serde_json::from_str(template_json()).unwrap(); - let path = &json.path("$..book[?(@.author size 10)].title").unwrap(); - - assert_eq!(path, &json!(["Sayings of the Century"])); -} -``` - -also, `JsonPathInst` can be used to query the data without cloning. -```rust -use serde_json::{json, Value}; -use crate::jsonpath_rust::{JsonPathInst}; - -fn test() { - let json: Value = serde_json::from_str("{}").expect("to get json"); - let query = JsonPathInst::from_str("$..book[?(@.author size 10)].title").unwrap(); - - // To convert to &Value, use deref() - assert_eq!(query.find_slice(&json).get(0).expect("to get value").deref(), &json!("Sayings of the Century")); -} -``` - -The library can return a path describing the value instead of the value itself. -To do that, the method `find_as_path` can be used: - -```rust -use jsonpath_rust::JsonPathFinder; -use serde_json::{json, Value, JsonPathValue}; - fn main() { - let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.first.second[?(@.active)]").unwrap(); - let slice_of_data: Value = finder.find_as_path(); - assert_eq!(slice_of_data, Value::Array(vec!["$.first.second[0]".to_string()])); -} -``` + let data = json!({"first":{"second":[{"active":1},{"passive":1}]}}); + let path = JsonPathInst::from_str("$.first.second[?(@.active)]").unwrap(); + let slice_of_data = jsonpath_rust::find_slice(&path, &data); -or it can be taken from the `JsonPathValue` instance: -```rust -use serde_json::{json, Value}; -use crate::jsonpath_rust::{JsonPathFinder, JsonPathQuery, JsonPathInst, JsonPathValue}; -use std::str::FromStr; - -fn test() { - let json: Box = serde_json::from_str("{}").unwrap(); - let path: Box = Box::from(JsonPathInst::from_str("$..book[?(@.author size 10)].title").unwrap()); - let finder = JsonPathFinder::new(json, path); + let expected_value = json!({"active":1}); + let expected_path = "$.['first'].['second'][0]".to_string(); - let v = finder.find_slice(); - let js = json!("Sayings of the Century"); - - // Slice has a path of its value as well - assert_eq!(v, vec![JsonPathValue::Slice(&js,"$.book[0].title".to_string())]); + assert_eq!( + slice_of_data, + vec![JsonPathValue::Slice(&expected_value, expected_path)] + ); } ``` -** If the value has been modified during the search, there is no way to find a path of a new value. -It can happen if we try to find a length() of array, for in stance.** - +### The structure +The internal structure of the `JsonPath` can be found here: +https://docs.rs/jsonpath-rust/latest/jsonpath_rust/parser/model/enum.JsonPath.html -## The structure - -```rust -pub enum JsonPath { - Root, - // <- $ - Field(String), - // <- field of the object - Chain(Vec), - // <- the whole jsonpath - Descent(String), - // <- '..' - Index(JsonPathIndex), - // <- the set of indexes represented by the next structure [[JsonPathIndex]] - Current(Box), - // <- @ - Wildcard, - // <- * - Empty, // the structure to avoid inconsistency -} - -pub enum JsonPathIndex { - Single(usize), - // <- [1] - UnionIndex(Vec), - // <- [1,2,3] - UnionKeys(Vec), - // <- ['key_1','key_2'] - Slice(i32, i32, usize), - // [0:10:1] - Filter(Operand, FilterSign, Operand), // <- [?(operand sign operand)] -} - -``` +The internal structure of the `JsonPathIndex` can be found here: +https://docs.rs/jsonpath-rust/latest/jsonpath_rust/parser/model/enum.JsonPathIndex.html ## How to contribute @@ -434,4 +297,4 @@ TBD - update files - commit them - add tag `git tag -a v -m "message"` - - git push origin \ No newline at end of file + - git push origin diff --git a/src/lib.rs b/src/lib.rs index 1e0fd25..0cc09eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -397,6 +397,27 @@ impl<'a, Data> JsonPathValue<'a, Data> { /// finds a slice of data in the set json. /// The result is a vector of references to the incoming structure. +/// +/// In case, if there is no match `find_slice` will return `vec![NoValue]`. +/// +/// ## Example +/// ```rust +/// use jsonpath_rust::{JsonPathInst, JsonPathValue}; +/// use serde_json::json; +/// # use std::str::FromStr; +/// +/// let data = json!({"first":{"second":[{"active":1},{"passive":1}]}}); +/// let path = JsonPathInst::from_str("$.first.second[?(@.active)]").unwrap(); +/// let slice_of_data = jsonpath_rust::find_slice(&path, &data); +/// +/// let expected_value = json!({"active":1}); +/// let expected_path = "$.['first'].['second'][0]".to_string(); +/// +/// assert_eq!( +/// slice_of_data, +/// vec![JsonPathValue::Slice(&expected_value, expected_path)] +/// ); +/// ``` pub fn find_slice<'a>(path: &'a JsonPathInst, json: &'a Value) -> Vec> { let instance = json_path_instance(&path.inner, json); let res = instance.find(JsonPathValue::from_root(json)); @@ -411,6 +432,21 @@ pub fn find_slice<'a>(path: &'a JsonPathInst, json: &'a Value) -> Vec Value { let slice = find_slice(path, json); if !slice.is_empty() { @@ -429,8 +465,26 @@ pub fn find(path: &JsonPathInst, json: &Value) -> Value { Value::Array(vec![]) } } -/// finds a path of the values. + +/// finds a path describing the value, instead of the value itself. /// If the values has been obtained by moving the data out of the initial json the path is absent. +/// +/// ** If the value has been modified during the search, there is no way to find a path of a new value. +/// It can happen if we try to find a length() of array, for in stance.** +/// +/// ## Example +/// ```rust +/// use jsonpath_rust::{JsonPathInst, JsonPathValue}; +/// use serde_json::{Value, json}; +/// # use std::str::FromStr; +/// +/// let data = json!({"first":{"second":[{"active":1},{"passive":1}]}}); +/// let path = JsonPathInst::from_str("$.first.second[?(@.active)]").unwrap(); +/// let slice_of_data: Value = jsonpath_rust::find_as_path(&path, &data); +/// +/// let expected_path = "$.['first'].['second'][0]".to_string(); +/// assert_eq!(slice_of_data, Value::Array(vec![Value::String(expected_path)])); +/// ``` pub fn find_as_path(path: &JsonPathInst, json: &Value) -> Value { Value::Array( find_slice(path, json)