From 7cd697cdb5fbfa360d2e60bfa9aeb9a9d8b8d99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Tue, 5 Dec 2023 17:47:47 +0100 Subject: [PATCH] try to get rid of JsonPathFinder and Boxes - JsonPathFinder interface does not really benefit from storing the json or path internally - trying to get rid of the Box<> that is used inside of JsonPathFinder --- src/lib.rs | 348 ++++++++++++++++++++--------------------------------- 1 file changed, 133 insertions(+), 215 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aba2969..8a3c316 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,31 +81,17 @@ //! //! # Examples //!```rust -//! use serde_json::{json,Value}; -//! use jsonpath_rust::jp_v; -//! use self::jsonpath_rust::JsonPathFinder; -//! use self::jsonpath_rust::JsonPathValue; +//! use std::str::FromStr; +//! use serde_json::{json, Value}; +//! use jsonpath_rust::{jp_v, find_slice, JsonPathValue, JsonPathInst}; //! -//! fn test(){ -//! let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.first.second[?(@.active)]").unwrap(); -//! let slice_of_data:Vec> = finder.find_slice(); +//! fn test() -> Result<(), Box> { +//! let json = serde_json::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#)?; +//! let path = JsonPathInst::from_str("$.first.second[?(@.active)]")?; +//! let slice_of_data:Vec> = find_slice(&path, &json); //! let js = json!({"active":2}); //! assert_eq!(slice_of_data, jp_v![&js;"$.first.second[0]",]); -//! } -//! ``` -//! or even simpler: -//! -//!``` -//! use serde_json::{json,Value}; -//! use self::jsonpath_rust::JsonPathFinder; -//! use self::jsonpath_rust::JsonPathValue; -//! fn test(json: &str, path: &str, expected: Vec>) { -//! match JsonPathFinder::from_str(json, path) { -//! Ok(finder) => assert_eq!(finder.find_slice(), expected), -//! Err(e) => panic!("error while parsing json or jsonpath: {}", e) -//! } -//! -//! +//! # Ok(()) //! } //! ``` //! @@ -116,7 +102,7 @@ use crate::parser::model::JsonPath; use crate::parser::parser::parse_json_path; -use crate::path::{json_path_instance, PathInstance}; +use crate::path::{json_path_instance}; use serde_json::Value; use std::convert::TryInto; use std::fmt::Debug; @@ -137,27 +123,28 @@ extern crate pest; /// # Examples: /// ``` /// use std::str::FromStr; -/// use serde_json::{json,Value}; +/// use serde_json::{json, Value}; /// use jsonpath_rust::jp_v; -/// use crate::jsonpath_rust::{JsonPathFinder,JsonPathQuery,JsonPathInst,JsonPathValue}; -///fn test(){ -/// let json: Value = serde_json::from_str("{}").unwrap(); -/// let v = json.path("$..book[?(@.author size 10)].title").unwrap(); -/// assert_eq!(v, json!([])); +/// use jsonpath_rust::{find_slice, JsonPathQuery, JsonPathInst, JsonPathValue}; /// -/// let json: Value = serde_json::from_str("{}").unwrap(); -/// let path = json.path("$..book[?(@.author size 10)].title").unwrap(); +/// fn test() -> Result<(), Box> { +/// let json: Value = serde_json::from_str("{}")?; +/// let v = json.path("$..book[?(@.author size 10)].title")?; +/// assert_eq!(v, json!([])); /// -/// assert_eq!(path, json!(["Sayings of the Century"])); +/// let json: Value = serde_json::from_str("{}")?; +/// let path = json.path("$..book[?(@.author size 10)].title")?; /// -/// 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); +/// assert_eq!(path, json!(["Sayings of the Century"])); /// -/// let v = finder.find_slice(); -/// let js = json!("Sayings of the Century"); -/// assert_eq!(v, jp_v![&js;"",]); -/// } +/// let json: Value = serde_json::from_str("{}")?; +/// let path: JsonPathInst = JsonPathInst::from_str("$..book[?(@.author size 10)].title")?; +/// +/// let v = find_slice(&path, &json); +/// let js = json!("Sayings of the Century"); +/// assert_eq!(v, jp_v![&js;"",]); +/// # Ok(()) +/// } /// /// ``` /// #Note: @@ -217,40 +204,44 @@ impl<'a> Deref for JsonPtr<'a, Value> { } } -impl JsonPathQuery for Box { +impl JsonPathQuery for Value { fn path(self, query: &str) -> Result { let p = JsonPathInst::from_str(query)?; - Ok(JsonPathFinder::new(self, Box::new(p)).find()) + Ok(find(&p, &self)) } } -impl JsonPathQuery for Value { +/* +impl JsonPathQuery for T + where T: Deref { fn path(self, query: &str) -> Result { let p = JsonPathInst::from_str(query)?; - Ok(JsonPathFinder::new(Box::new(self), Box::new(p)).find()) + Ok(find(&p, self.deref())) } } + */ /// just to create a json path value of data /// Example: -/// - json_path_value(&json) = `JsonPathValue::Slice(&json)` -/// - json_path_value(&json,) = `vec![JsonPathValue::Slice(&json)]` -/// - `json_path_value[&json1,&json1]` = `vec![JsonPathValue::Slice(&json1),JsonPathValue::Slice(&json2)]` -/// - json_path_value(json) = `JsonPathValue::NewValue(json)` +/// - `jp_v(&json) = JsonPathValue::Slice(&json)` +/// - `jp_v(&json;"foo") = JsonPathValue::Slice(&json, "foo".to_string())` +/// - `jp_v(&json,) = vec![JsonPathValue::Slice(&json)]` +/// - `jp_v[&json1,&json1] = vec![JsonPathValue::Slice(&json1),JsonPathValue::Slice(&json2)]` +/// - `jp_v(json) = JsonPathValue::NewValue(json)` /// ``` /// use std::str::FromStr; -/// use serde_json::{json,Value}; -/// use jsonpath_rust::jp_v; -/// use crate::jsonpath_rust::{JsonPathFinder,JsonPathQuery,JsonPathInst,JsonPathValue}; -///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); +/// use serde_json::{json, Value}; +/// use jsonpath_rust::{jp_v, find_slice, JsonPathQuery, JsonPathInst, JsonPathValue}; +/// +/// fn test() -> Result<(), Box> { +/// let json: Value = serde_json::from_str("{}")?; +/// let path: JsonPathInst = JsonPathInst::from_str("$..book[?(@.author size 10)].title")?; +/// let v = find_slice(&path, &json); /// -/// let v = finder.find_slice(); -/// let js = json!("Sayings of the Century"); -/// assert_eq!(v, jp_v![&js;"",]); -/// } +/// let js = json!("Sayings of the Century"); +/// assert_eq!(v, jp_v![&js;"",]); +/// # Ok(()) +/// } /// ``` #[macro_export] macro_rules! jp_v { @@ -258,9 +249,9 @@ macro_rules! jp_v { JsonPathValue::Slice(&$v, String::new()) }; - (&$v:expr ; $s:expr) =>{ + (&$v:expr ; $s:expr) =>{ JsonPathValue::Slice(&$v, $s.to_string()) - }; + }; ($(&$v:expr;$s:expr),+ $(,)?) =>{ { @@ -403,109 +394,73 @@ impl<'a, Data> JsonPathValue<'a, Data> { } } -/// The base structure stitching the json instance and jsonpath instance -pub struct JsonPathFinder { - json: Box, - path: Box, -} - -impl JsonPathFinder { - /// creates a new instance of [JsonPathFinder] - pub fn new(json: Box, path: Box) -> Self { - JsonPathFinder { json, path } - } - - /// updates a path with a new one - pub fn set_path(&mut self, path: Box) { - self.path = path - } - /// updates a json with a new one - pub fn set_json(&mut self, json: Box) { - self.json = json - } - /// updates a json from string and therefore can be some parsing errors - pub fn set_json_str(&mut self, json: &str) -> Result<(), String> { - self.json = serde_json::from_str(json).map_err(|e| e.to_string())?; - Ok(()) - } - /// updates a path from string and therefore can be some parsing errors - pub fn set_path_str(&mut self, path: &str) -> Result<(), String> { - self.path = Box::new(JsonPathInst::from_str(path)?); - Ok(()) - } - - /// create a new instance from string and therefore can be some parsing errors - pub fn from_str(json: &str, path: &str) -> Result { - let json = serde_json::from_str(json).map_err(|e| e.to_string())?; - let path = Box::new(JsonPathInst::from_str(path)?); - Ok(JsonPathFinder::new(json, path)) - } - - /// creates an instance to find a json slice from the json - pub fn instance(&self) -> PathInstance { - json_path_instance(&self.path.inner, &self.json) - } - /// finds a slice of data in the set json. - /// The result is a vector of references to the incoming structure. - pub fn find_slice(&self) -> Vec> { - let res = self.instance().find(JsonPathValue::from_root(&self.json)); - let has_v: Vec> = - res.into_iter().filter(|v| v.has_value()).collect(); - - if has_v.is_empty() { - vec![NoValue] - } else { - has_v - } +/// finds a slice of data in the set json. +/// The result is a vector of references to the incoming structure. +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)); + let has_v: Vec> = + res.into_iter().filter(|v| v.has_value()).collect(); + + if has_v.is_empty() { + vec![NoValue] + } else { + has_v } +} - /// finds a slice of data and wrap it with Value::Array by cloning the data. - /// Returns either an array of elements or Json::Null if the match is incorrect. - pub fn find(&self) -> Value { - let slice = self.find_slice(); - if !slice.is_empty() { - if JsonPathValue::only_no_value(&slice) { - Value::Null - } else { - Value::Array( - self.find_slice() - .into_iter() - .filter(|v| v.has_value()) - .map(|v| v.to_data()) - .collect(), - ) - } +/// finds a slice of data and wrap it with Value::Array by cloning the data. +/// Returns either an array of elements or Json::Null if the match is incorrect. +pub fn find(path: &JsonPathInst, json: &Value) -> Value { + let slice = find_slice(&path, &json); + if !slice.is_empty() { + if JsonPathValue::only_no_value(&slice) { + Value::Null } else { - Value::Array(vec![]) + Value::Array( + slice + .into_iter() + .filter(|v| v.has_value()) + .map(|v| v.to_data()) + .collect(), + ) } - } - /// finds a path of the values. - /// If the values has been obtained by moving the data out of the initial json the path is absent. - pub fn find_as_path(&self) -> Value { - Value::Array( - self.find_slice() - .into_iter() - .flat_map(|v| v.to_path()) - .map(|v| v.into()) - .collect(), - ) + } else { + Value::Array(vec![]) } } +/// finds a path of the values. +/// If the values has been obtained by moving the data out of the initial json the path is absent. +pub fn find_as_path(path: &JsonPathInst, json: &Value) -> Value { + Value::Array( + find_slice(path, json) + .into_iter() + .flat_map(|v| v.to_path()) + .map(|v| v.into()) + .collect(), + ) +} #[cfg(test)] mod tests { use crate::JsonPathQuery; use crate::JsonPathValue::{NoValue, Slice}; - use crate::{jp_v, JsonPathFinder, JsonPathInst, JsonPathValue}; + use crate::{jp_v, JsonPathInst, JsonPathValue}; use serde_json::{json, Value}; use std::ops::Deref; use std::str::FromStr; fn test(json: &str, path: &str, expected: Vec>) { - match JsonPathFinder::from_str(json, path) { - Ok(finder) => assert_eq!(finder.find_slice(), expected), - Err(e) => panic!("error while parsing json or jsonpath: {}", e), - } + let json: Value = match serde_json::from_str(json) { + Ok(json) => json, + Err(e) => panic!("error while parsing json: {}", e), + }; + let path = match JsonPathInst::from_str(path) { + Ok(path) => path, + Err(e) => panic!("error while parsing jsonpath: {}", e), + }; + + assert_eq!(super::find_slice(&path, &json), expected) } fn template_json<'a>() -> &'a str { @@ -920,9 +875,7 @@ mod tests { JsonPathInst::from_str("$..book[?(@.author size 10)].title") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); let js = json!("Sayings of the Century"); assert_eq!(v, jp_v![&js;"$.['store'].['book'][0].['title']",]); } @@ -933,9 +886,7 @@ mod tests { let path: Box = Box::from( JsonPathInst::from_str("$.[?(@.verb == 'TEST')]").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); let js = json!({"verb":"TEST"}); assert_eq!(v, jp_v![&js;"$[0]",]); } @@ -948,9 +899,7 @@ mod tests { JsonPathInst::from_str("$.[?(@.verb == 'TEST')].length()") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - - let v = finder.find(); + let v = super::find(&path, &json); let js = json!([2]); assert_eq!(v, js); @@ -958,8 +907,7 @@ mod tests { Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); let path: Box = Box::from(JsonPathInst::from_str("$.length()").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - assert_eq!(finder.find(), json!([3])); + assert_eq!(super::find(&path, &json), json!([3])); // length of search following the wildcard returns correct result let json: Box = @@ -968,37 +916,32 @@ mod tests { JsonPathInst::from_str("$.[?(@.verb == 'TEST')].[*].length()") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - assert_eq!(finder.find(), json!([3])); + assert_eq!(super::find(&path, &json), json!([3])); // length of object returns 0 let json: Box = Box::new(json!({"verb": "TEST"})); let path: Box = Box::from(JsonPathInst::from_str("$.length()").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - assert_eq!(finder.find(), Value::Null); + assert_eq!(super::find(&path, &json), Value::Null); // length of integer returns null let json: Box = Box::new(json!(1)); let path: Box = Box::from(JsonPathInst::from_str("$.length()").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - assert_eq!(finder.find(), Value::Null); + assert_eq!(super::find(&path, &json), Value::Null); // length of array returns correct result let json: Box = Box::new(json!([[1], [2], [3]])); let path: Box = Box::from(JsonPathInst::from_str("$.length()").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - assert_eq!(finder.find(), json!([3])); + assert_eq!(super::find(&path, &json), json!([3])); // path does not exist returns length null let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); let path: Box = Box::from(JsonPathInst::from_str("$.not.exist.length()").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - assert_eq!(finder.find(), Value::Null); + assert_eq!(super::find(&path, &json), Value::Null); // seraching one value returns correct length let json: Box = @@ -1006,9 +949,8 @@ mod tests { let path: Box = Box::from( JsonPathInst::from_str("$.[?(@.verb == 'RUN')].length()").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find(); + let v = super::find(&path, &json); let js = json!([1]); assert_eq!(v, js); @@ -1019,9 +961,8 @@ mod tests { JsonPathInst::from_str("$.[?(@.verb == 'RUN')].key123.length()") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find(); + let v = super::find(&path, &json); let js = json!(null); assert_eq!(v, js); @@ -1030,9 +971,8 @@ mod tests { Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); let path: Box = Box::from(JsonPathInst::from_str("$.[0].length()").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - let v = finder.find(); + let v = super::find(&path, &json); let js = Value::Null; assert_eq!(v, js); @@ -1041,9 +981,8 @@ mod tests { let path: Box = Box::from( JsonPathInst::from_str("$.[?(@.prop)].prop.[0].length()").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find(); + let v = super::find(&path, &json); let js = json!([3]); assert_eq!(v, js); @@ -1052,9 +991,8 @@ mod tests { let path: Box = Box::from( JsonPathInst::from_str("$.[?(@.prop)].prop.[1].length()").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find(); + let v = super::find(&path, &json); let js = Value::Null; assert_eq!(v, js); } @@ -1067,8 +1005,7 @@ mod tests { let path: Box = Box::from(JsonPathInst::from_str("$.field[1]").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); let json: Box = Box::new(json!({ @@ -1077,8 +1014,7 @@ mod tests { let path: Box = Box::from(JsonPathInst::from_str("$.field[1]").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); } @@ -1090,8 +1026,7 @@ mod tests { let path: Box = Box::from(JsonPathInst::from_str("$.field[?(@ == 0)]").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); } @@ -1104,8 +1039,7 @@ mod tests { let path: Box = Box::from( JsonPathInst::from_str("$.field[?(@.f_ == 0)]").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); } @@ -1117,8 +1051,7 @@ mod tests { let path: Box = Box::from(JsonPathInst::from_str("$..f_").expect("the path is correct")); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!( v, vec![Slice(&json!(1), "$.['field'][1].['f'].['f_']".to_string())] @@ -1133,15 +1066,13 @@ mod tests { let path: Box = Box::from(JsonPathInst::from_str("$.field_.field").expect("the path is correct")); - let finder = JsonPathFinder::new(json.clone(), path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); let path: Box = Box::from( JsonPathInst::from_str("$.field_.field[?(@ == 1)]").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); } @@ -1153,9 +1084,7 @@ mod tests { let path: Box = Box::from( JsonPathInst::from_str("$.[?(@.verb == 'RUN1')]").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - - let v = finder.find(); + let v = super::find(&path, &json); let js = json!(null); assert_eq!(v, js); } @@ -1169,8 +1098,7 @@ mod tests { let path: Box = Box::from( JsonPathInst::from_str("$.field.field.length()").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); let json: Box = Box::new(json!({ @@ -1179,8 +1107,7 @@ mod tests { let path: Box = Box::from( JsonPathInst::from_str("$.field[?(@.a == 0)].f.length()").expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); } @@ -1212,18 +1139,14 @@ mod tests { JsonPathInst::from_str("$.first[?(@.does_not_exist && @.does_not_exist >= 1.0)]") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json.clone(), path); - - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); let path: Box = Box::from( JsonPathInst::from_str("$.first[?(@.does_not_exist >= 1.0)]") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!(v, vec![NoValue]); } @@ -1237,9 +1160,8 @@ mod tests { JsonPathInst::from_str("$.[?(@.author ~= '(?i)d\\(Rees\\)')]") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json.clone(), path); assert_eq!( - finder.find_slice(), + super::find_slice(&path, &json.clone()), vec![Slice(&json!({"author":"abcd(Rees)"}), "$".to_string())] ); } @@ -1251,8 +1173,7 @@ mod tests { JsonPathInst::from_str("$.first[?(!@.does_not_exist >= 1.0)]") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json.clone(), path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!( v, vec![Slice( @@ -1265,8 +1186,7 @@ mod tests { JsonPathInst::from_str("$.first[?(!(@.does_not_exist >= 1.0))]") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json.clone(), path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!( v, vec![Slice( @@ -1279,8 +1199,7 @@ mod tests { JsonPathInst::from_str("$.first[?(!(@.second.active == 1) || @.second.active == 1)]") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json.clone(), path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!( v, vec![Slice( @@ -1293,8 +1212,7 @@ mod tests { JsonPathInst::from_str("$.first[?(!@.second.active == 1 && !@.second.active == 1 || !@.second.active == 2)]") .expect("the path is correct"), ); - let finder = JsonPathFinder::new(json, path); - let v = finder.find_slice(); + let v = super::find_slice(&path, &json); assert_eq!( v, vec![Slice(