diff --git a/README.md b/README.md index d8c7fc3..2652276 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,11 @@ It is an implementation for [JsonPath](https://goessner.net/articles/JsonPath/) - [Webassembly Demo](https://freestrings.github.io/jsonpath/) - [Rust documentation](https://docs.rs/jsonpath_lib/0.1.6/jsonpath_lib) -## 왜? +## Why? To enjoy Rust! -## 목차 +## API [With Javascript](#with-javascript) @@ -25,16 +25,17 @@ To enjoy Rust! - [javascript - jsonpath.compile(jsonpath: string)](#javascript---jsonpathcompilejsonpath-string) - [javascript - jsonpath.selector(json: string|object)](#javascript---jsonpathselectorjson-stringobject) - [javascript - alloc_json, dealloc_json](#javascript---alloc_json-dealloc_json) -- [javascript-wasm - examples](https://github.com/freestrings/jsonpath/wiki/javascript-wasm-examples) +- [javascript-wasm - examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples) [With Rust (as library)](#with-rust-as-library) - [jsonpath_lib library](#jsonpath_lib-library) - [rust - jsonpath::select(json: &serde_json::value::Value, jsonpath: &str)](#rust---jsonpathselectjson-serde_jsonvaluevalue-jsonpath-str) - [rust - jsonpath::select_as_str(json_str: &str, jsonpath: &str)](#rust---jsonpathselect_as_strjson-str-jsonpath-str) -- [rust - jsonpath::select_as\(json_str: &str, jsonpath: &str)](#rust---jsonpathselect_astjson-str-jsonpath-str) +- [rust - jsonpath::select_as\(json_str: &str, jsonpath: &str)](#rust---jsonpathselect_ast-serdededeserializeownedjson-str-jsonpath-str) - [rust - jsonpath::compile(jsonpath: &str)](#rust---jsonpathcompilejsonpath-str) - [rust - jsonpath::selector(json: &serde_json::value::Value)](#rust---jsonpathselectorjson-serde_jsonvaluevalue) +- [rust - jsonpath::selector_as\(json: &serde_json::value::Value)](#rust---jsonpathselector_ast-serdededeserializeownedjson-serde_jsonvaluevalue) - [rust - examples](https://github.com/freestrings/jsonpath/wiki/rust-examples) [With AWS API Gateway](#) @@ -118,9 +119,9 @@ console.log(JSON.stringify(template(jsonObj2)) == ret2); // 2. read as json string console.log(JSON.stringify(template(JSON.stringify(jsonObj2))) == ret2); ``` - + ### javascript - jsonpath.selector(json: string|object) - + ```javascript let jsonObj = { "school": { @@ -145,14 +146,13 @@ console.log(JSON.stringify(selector("$..friends[1]")) == ret2); ### javascript - alloc_json, dealloc_json -*(in `jsonpath-rs` not yet supported)* +*(not supported in `jsonpath-rs`)* wasm-bindgen은 Javascript와 Webassembly 간 값을 주고받을 때 JSON 객체는 String으로 변환되기 때문에, 반복해서 사용되는 JSON 객체를 Webassembly 영역에 생성해 두면 성능에 도움이 된다. Since wasm-bindgen converts JSON objects to String when exchanging values between Javascript and Webassembly, it is helpful to create repeated Json objects in Webassembly area. ```javascript - let jsonObj = { "school": { "friends": [{"id": 0}, {"id": 1}] @@ -183,7 +183,6 @@ console.log( JSON.stringify(ret1) == JSON.stringify(ret6));// true jsonpath.dealloc_json(ptr); - ``` ## With Rust (as library) @@ -201,34 +200,56 @@ extern crate serde_json; ```rust let json_obj = json!({ "school": { - "friends": [{"id": 0}, {"id": 1}] + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] }, - "friends": [{"id": 0}, {"id": 1}] -}); -let json = jsonpath::select(json_obj, "$..friends[0]").unwrap(); -let ret = json!([ {"id": 0}, {"id": 0} ]); -assert_eq!(json, ret) + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]}); + +let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap(); + +let ret = json!([ + {"name": "친구3", "age": 30}, + {"name": "친구1", "age": 20} +]); +assert_eq!(json, ret); ``` ### rust - jsonpath::select_as_str(json: &str, jsonpath: &str) ```rust -let ret = jsonpath::select_as_str(r#"{ - "school": { "friends": [{"id": 0}, {"id": 1}] }, - "friends": [{"id": 0}, {"id": 1}] -}"#, "$..friends[0]").unwrap(); -assert_eq!(ret, r#"[{"id":0},{"id":0}]"#); +let ret = jsonpath::select_as_str(r#" +{ + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ] +} +"#, "$..friends[0]").unwrap(); + +assert_eq!(ret, r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#); ``` -### rust - jsonpath::select_as\(json: &str, jsonpath: &str) +### rust - jsonpath::select_as\(json: &str, jsonpath: &str) ```rust -#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[derive(Deserialize, PartialEq, Debug)] struct Person { name: String, age: u8, phones: Vec, } + let ret: Person = jsonpath::select_as(r#" { "person": @@ -242,11 +263,13 @@ let ret: Person = jsonpath::select_as(r#" } } "#, "$.person").unwrap(); + let person = Person { name: "Doe John".to_string(), age: 44, phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()], }; + assert_eq!(person, ret); ``` @@ -257,44 +280,99 @@ let mut template = jsonpath::compile("$..friends[0]"); let json_obj = json!({ "school": { - "friends": [ {"id": 0}, {"id": 1} ] + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] }, - "friends": [ {"id": 0}, {"id": 1} ] -}); + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]}); let json = template(&json_obj).unwrap(); -let ret = json!([ {"id": 0}, {"id": 0} ]); -assert_eq!(json, ret); -let json_obj = json!({ - "school": { - "friends": [ {"name": "Millicent Norman"}, {"name": "Vincent Cannon"} ] - }, - "friends": [ {"id": 0}, {"id": 1} ] -}); +let ret = json!([ + {"name": "친구3", "age": 30}, + {"name": "친구1", "age": 20} +]); -let json = template(json_obj).unwrap(); -let ret = json!([ {"id": 0}, {"name": "Millicent Norman"} ]); assert_eq!(json, ret); ``` -### rust - jsonpath::selector(&json: serde_json::value::Value) +### rust - jsonpath::selector(json: &serde_json::value::Value) ```rust let json_obj = json!({ "school": { - "friends": [{"id": 0}, {"id": 1}] + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] }, - "friends": [{"id": 0},{"id": 1}] -}); + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]}); let mut selector = jsonpath::selector(&json_obj); let json = selector("$..friends[0]").unwrap(); -let ret = json!([ {"id": 0}, {"id": 0} ]); + +let ret = json!([ + {"name": "친구3", "age": 30}, + {"name": "친구1", "age": 20} +]); + assert_eq!(json, ret); let json = selector("$..friends[1]").unwrap(); -let ret = json!([ {"id": 1}, {"id": 1} ]); + +let ret = json!([ + {"name": "친구4"}, + {"name": "친구2", "age": 20} +]); + assert_eq!(json, ret); ``` + +### rust - jsonpath::selector_as\(json: &serde_json::value::Value) + +```rust +let json_obj = json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]}); + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct Friend { + name: String, + age: Option, +} + +let mut selector = jsonpath::selector_as::>(&json_obj); + +let json = selector("$..friends[0]").unwrap(); + +let ret = vec!( + Friend { name: "친구3".to_string(), age: Some(30) }, + Friend { name: "친구1".to_string(), age: Some(20) } +); +assert_eq!(json, ret); + +let json = selector("$..friends[1]").unwrap(); + +let ret = vec!( + Friend { name: "친구4".to_string(), age: None }, + Friend { name: "친구2".to_string(), age: Some(20) } +); + +assert_eq!(json, ret); +``` \ No newline at end of file diff --git a/benches/bench.rs b/benches/bench.rs index eaf722d..4b2b0cd 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -42,6 +42,17 @@ fn bench_selector(b: &mut Bencher) { }); } +#[bench] +fn bench_selector_as(b: &mut Bencher) { + let json = get_json(); + let mut selector = jsonpath::selector_as::(&json); + b.iter(move || { + for _ in 1..100 { + let _ = selector(get_path()).unwrap(); + } + }); +} + #[bench] fn bench_select_val(b: &mut Bencher) { let json = get_json(); @@ -53,7 +64,7 @@ fn bench_select_val(b: &mut Bencher) { } #[bench] -fn bench_select_str(b: &mut Bencher) { +fn bench_select_as_str(b: &mut Bencher) { let json = get_string(); b.iter(move || { for _ in 1..100 { @@ -73,7 +84,6 @@ fn bench_compile(b: &mut Bencher) { }); } - #[bench] fn bench_select_as(b: &mut Bencher) { let json = get_string(); @@ -83,7 +93,7 @@ fn bench_select_as(b: &mut Bencher) { category: String, author: String, title: String, - price: f64 + price: f64, } b.iter(move || { diff --git a/src/filter/value_filter.rs b/src/filter/value_filter.rs index fecafc3..b6096ac 100644 --- a/src/filter/value_filter.rs +++ b/src/filter/value_filter.rs @@ -277,9 +277,9 @@ pub struct JsonValueFilter { impl JsonValueFilter { pub fn new(json: &str) -> Result { - let json: Value = serde_json::from_str(json) + let json: RefValue = serde_json::from_str(json) .map_err(|e| e.description().to_string())?; - Ok(JsonValueFilter::new_from_value((&json).into())) + Ok(JsonValueFilter::new_from_value(json .into())) } pub fn new_from_value(json: RefValueWrapper) -> Self { diff --git a/src/lib.rs b/src/lib.rs index fd26e8e..6410469 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,6 +158,7 @@ //! assert_eq!(ret, json); //! ``` +extern crate env_logger; extern crate indexmap; #[macro_use] extern crate log; @@ -165,6 +166,7 @@ extern crate log; extern crate serde; extern crate serde_json; +use std::error::Error; use std::ops::Deref; use std::result; @@ -172,7 +174,7 @@ use serde_json::Value; use filter::value_filter::JsonValueFilter; use parser::parser::{NodeVisitor, Parser}; -use ref_value::model::{RefValue, RefValueWrapper}; +use ref_value::model::RefValueWrapper; #[doc(hidden)] pub mod parser; @@ -181,7 +183,23 @@ pub mod filter; #[doc(hidden)] pub mod ref_value; -/// # Compile a Jsonpath so it select a JsonObject immediately. +fn query_from_str(json: &str, path: &str) -> result::Result { + let mut jf = JsonValueFilter::new(json)?; + let mut parser = Parser::new(path); + parser.parse(&mut jf)?; + Ok(jf) +} + +fn query_from_json_wrapper(json_wrapper: RefValueWrapper, path: &str) -> result::Result { + let mut jf = JsonValueFilter::new_from_value(json_wrapper); + let mut parser = Parser::new(path); + parser.parse(&mut jf)?; + Ok(jf) +} + +/// It is a highorder function that compile a JsonPath then returns a function. +/// +/// this return function can be reused for different JsonObjects. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; @@ -189,28 +207,23 @@ pub mod ref_value; /// /// let mut template = jsonpath::compile("$..friends[0]"); /// -/// /// let json_obj = json!({ /// "school": { -/// "friends": [ {"id": 0}, {"id": 1} ] +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] /// }, -/// "friends": [ {"id": 0}, {"id": 1} ] -/// }); +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]}); /// /// let json = template(&json_obj).unwrap(); -/// let ret = json!([ {"id": 0}, {"id": 0} ]); -/// assert_eq!(json, ret); -/// -/// -/// let json_obj = json!({ -/// "school": { -/// "friends": [ {"name": "Millicent Norman"}, {"name": "Vincent Cannon"} ] -/// }, -/// "friends": [ {"id": 0}, {"id": 1} ] -/// }); -/// -/// let json = template(&json_obj).unwrap(); -/// let ret = json!([ {"id": 0}, {"name": "Millicent Norman"} ]); +/// let ret = json!([ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구1", "age": 20} +/// ]); /// assert_eq!(json, ret); /// ``` pub fn compile<'a>(path: &'a str) -> impl FnMut(&Value) -> result::Result + 'a { @@ -221,15 +234,16 @@ pub fn compile<'a>(path: &'a str) -> impl FnMut(&Value) -> result::Result { let mut jf = JsonValueFilter::new_from_value(json.into()); jf.visit(n.clone()); - Ok(jf.take_value().into()) + Ok((&jf.take_value()).into()) } Err(e) => Err(e.clone()) } } } - -/// # Use multiple JsonPaths for one JsonObject. +/// It returns highorder function that return a function. +/// +/// this function has a jsonpath as argument and return a serde_json::value::Value. so you can use different JsonPath for one JsonObject. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; @@ -237,28 +251,90 @@ pub fn compile<'a>(path: &'a str) -> impl FnMut(&Value) -> result::Result impl FnMut(&str) -> result::Result { let wrapper: RefValueWrapper = json.into(); move |path: &str| { - let mut jf = JsonValueFilter::new_from_value(wrapper.clone()); - let mut parser = Parser::new(path); - parser.parse(&mut jf)?; - Ok(jf.take_value().into()) + let mut jf = query_from_json_wrapper(wrapper.clone(), path)?; + Ok((&jf.take_value()).into()) + } +} + +/// It returns highorder function that returns a function. +/// +/// this function has a jsonpath as argument and return a serde::Deserialize. so you can use different JsonPath for one JsonObject. +/// +/// ```rust +/// extern crate jsonpath_lib as jsonpath; +/// extern crate serde; +/// #[macro_use] extern crate serde_json; +/// +/// use serde::{Deserialize, Serialize}; +/// +/// let json_obj = json!({ +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]}); +/// +/// #[derive(Serialize, Deserialize, PartialEq, Debug)] +/// struct Friend { +/// name: String, +/// age: Option, +/// } +/// +/// let mut selector = jsonpath::selector_as::>(&json_obj); +/// +/// let json = selector("$..friends[0]").unwrap(); +/// let ret = vec!( +/// Friend { name: "친구3".to_string(), age: Some(30) }, +/// Friend { name: "친구1".to_string(), age: Some(20) } +/// ); +/// assert_eq!(json, ret); +/// +/// let json = selector("$..friends[1]").unwrap(); +/// let ret = vec!( +/// Friend { name: "친구4".to_string(), age: None }, +/// Friend { name: "친구2".to_string(), age: Some(20) } +/// ); +/// assert_eq!(json, ret); +/// ``` +pub fn selector_as(json: &Value) -> impl FnMut(&str) -> result::Result { + let wrapper: RefValueWrapper = json.into(); + move |path: &str| { + let mut jf = query_from_json_wrapper(wrapper.clone(), path)?; + T::deserialize(jf.take_value().deref()).map_err(|e| format!("{:?}", e)) } } @@ -267,7 +343,7 @@ pub fn reader(json: &Value) -> impl FnMut(&str) -> result::Result selector(json) } -/// # Select a JsonObject +/// Select a JsonObject. it return a serde_json::value::Value. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; @@ -275,19 +351,27 @@ pub fn reader(json: &Value) -> impl FnMut(&str) -> result::Result /// /// let json_obj = json!({ /// "school": { -/// "friends": [{"id": 0}, {"id": 1}] +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] /// }, -/// "friends": [{"id": 0}, {"id": 1}] -/// }); +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]}); +/// /// let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap(); -/// let ret = json!([ {"id": 0}, {"id": 0} ]); +/// +/// let ret = json!([ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구1", "age": 20} +/// ]); /// assert_eq!(json, ret); /// ``` pub fn select(json: &Value, path: &str) -> result::Result { - let mut jf = JsonValueFilter::new_from_value(json.into()); - let mut parser = Parser::new(path); - parser.parse(&mut jf)?; - Ok(jf.take_value().into()) + let mut jf = query_from_json_wrapper(json.into(), path)?; + Ok((&jf.take_value()).into()) } #[deprecated(since = "0.1.4", note = "Please use the select function instead")] @@ -300,27 +384,36 @@ pub fn select_str(json: &str, path: &str) -> result::Result { select_as_str(json, path) } -/// # Return to json string +/// Select a JsonObject. it return a JsonObject as String. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; /// #[macro_use] extern crate serde_json; /// -/// let ret = jsonpath::select_as_str(r#"{ -/// "school": { "friends": [{"id": 0}, {"id": 1}] }, -/// "friends": [{"id": 0}, {"id": 1}] -/// }"#, "$..friends[0]").unwrap(); -/// assert_eq!(ret, r#"[{"id":0},{"id":0}]"#); +/// let ret = jsonpath::select_as_str(r#" +/// { +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ] +/// } +/// "#, "$..friends[0]").unwrap(); +/// +/// assert_eq!(ret, r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#); /// ``` pub fn select_as_str(json: &str, path: &str) -> result::Result { - let ref_value: RefValue = serde_json::from_str(json).map_err(|e| format!("{:?}", e))?; - let mut jf = JsonValueFilter::new_from_value(ref_value.into()); - let mut parser = Parser::new(path); - parser.parse(&mut jf)?; - serde_json::to_string(&jf.take_value().deref()).map_err(|e| format!("{:?}", e)) + let mut jf = query_from_str(json, path)?; + serde_json::to_string(&jf.take_value().deref()).map_err(|e| e.description().to_string()) } -/// # Return to deserializeable. +/// Select a JsonObject. it return a deserialized instance of type `T` +/// /// ```rust /// extern crate jsonpath_lib as jsonpath; /// extern crate serde; @@ -357,10 +450,7 @@ pub fn select_as_str(json: &str, path: &str) -> result::Result { /// /// assert_eq!(person, ret); /// ``` -pub fn select_as<'a, T: serde::Deserialize<'a>>(json: &str, path: &str) -> result::Result { - let ref_value: RefValue = serde_json::from_str(json).map_err(|e| format!("{:?}", e))?; - let mut jf = JsonValueFilter::new_from_value(ref_value.into()); - let mut parser = Parser::new(path); - parser.parse(&mut jf)?; - T::deserialize(jf.take_value().deref()).map_err(|e| format!("{:?}", e)) +pub fn select_as(json: &str, path: &str) -> result::Result { + let mut jf = query_from_str(json, path)?; + T::deserialize(jf.take_value().deref()).map_err(|e| e.description().to_string()) } \ No newline at end of file diff --git a/src/ref_value/model.rs b/src/ref_value/model.rs index 24c7907..48a7852 100644 --- a/src/ref_value/model.rs +++ b/src/ref_value/model.rs @@ -254,15 +254,6 @@ impl Into for &Value { } } -impl Into for RefValueWrapper { - fn into(self) -> Value { - match serde_json::to_value(self.deref()) { - Ok(v) => v, - Err(e) => panic!("Error RefValueWrapper into Value: {:?}", e) - } - } -} - impl Into for &RefValueWrapper { fn into(self) -> Value { match serde_json::to_value(self.deref()) { diff --git a/tests/lib.rs b/tests/lib.rs index 2fc9bbd..6fa8fb0 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -63,6 +63,33 @@ fn selector() { assert_eq!(json, ret); } +#[test] +fn selector_as() { + let json_obj = read_json("./benches/data_obj.json"); + let mut selector = jsonpath::selector_as::>(&json_obj); + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Friend { + id: u8, + name: Option, + } + + let json = selector("$..friends[2]").unwrap(); + + let ret = vec!( + Friend { id: 2, name: Some("Gray Berry".to_string()) }, + Friend { id: 2, name: Some("Gray Berry".to_string()) }, + ); + assert_eq!(json, ret); + + let json = selector("$..friends[0]").unwrap(); + let ret = vec!( + Friend { id: 0, name: None }, + Friend { id: 0, name: Some("Millicent Norman".to_string()) }, + ); + assert_eq!(json, ret); +} + #[test] fn select() { let json_obj = read_json("./benches/example.json"); diff --git a/tests/serde.rs b/tests/serde.rs index ff6511c..5723ac0 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -19,7 +19,7 @@ fn de() { let json_str = read_json("./benches/example.json"); // RefValue -> Value let ref_value: RefValue = serde_json::from_str(json_str.as_str()).unwrap(); - let value_wrapper: RefValueWrapper = ref_value.into(); + let ref value_wrapper: RefValueWrapper = ref_value.into(); let value: Value = value_wrapper.into(); // Value