Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4591803
change impl to support json path on generic value
May 4, 2021
b84cf04
make select and parser public
May 6, 2021
0f49e51
added the ability to get results with paths
May 13, 2021
f9e4a03
review fixes
May 27, 2021
1936a6d
added as_str to select value trait
Jun 20, 2021
a1fb41a
change keys and values to return iterators
Jun 23, 2021
392d9d5
changed &String to &str
Jun 23, 2021
6836df3
added items to SelectValue trait
Jun 24, 2021
eb11959
small optimization
Jun 24, 2021
81586c7
change tokenizer to run on slices instead of copy the values
Jul 20, 2021
0186730
return all tests
Jul 20, 2021
b542b07
review fixes
Aug 2, 2021
9064603
using tokenizer as iterator instead of creating an array
Aug 3, 2021
677575c
Update src/parser/tokenizer.rs
Aug 5, 2021
14af7aa
Merge pull request #7 from RedisJSON/slice_tokenizer
Aug 5, 2021
cba021e
Add API select_values_with_paths
oshadmi Oct 20, 2021
158392e
Fix compute_paths for select_values_with_paths
oshadmi Oct 21, 2021
0f63972
Fix compute_paths - per review
oshadmi Oct 25, 2021
4b11540
Add get_type_name api with default impl.
oshadmi Oct 25, 2021
8bc230c
Revert "Add get_type_name api with default impl."
oshadmi Oct 26, 2021
3f61db0
add assert (all values were found)
oshadmi Oct 27, 2021
ddb3b2d
Merge pull request #18 from RedisJSON/omer-multipath-api
oshadmi Oct 28, 2021
ee22024
Avoid panic on unimplemeted filter syntax (#19)
oshadmi Oct 31, 2021
1980433
ijson support (#20)
gkorland Dec 8, 2021
f768d20
MOD-2099: Avoid crash due to bad/unsupported filter (#22)
oshadmi Feb 24, 2022
f2525e9
Break after the first equals result_value to avoid duplicates
gkorland Mar 14, 2022
b4b07ee
Add test
gkorland Mar 14, 2022
ebfb0d1
Create rust.yml
gkorland Mar 14, 2022
9035411
Merge branch 'generic_json_path' into gkorland-duplicate
gkorland Mar 14, 2022
6da271e
Merge pull request #29 from RedisJSON/gkorland-duplicate
gkorland Mar 14, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Rust

on:
push:
branches: [ master, generic_json_path ]
pull_request:
branches: [ master, generic_json_path ]

env:
CARGO_TERM_COLOR: always

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ travis-ci = { repository = "freestrings/jsonpath", branch = "master" }

[dependencies]
log = "0.4"
ijson = "0.1.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
array_tool = "1.0.3"
Expand Down
155 changes: 89 additions & 66 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,20 +128,22 @@ extern crate core;
extern crate log;
extern crate serde;
extern crate serde_json;
extern crate ijson;

use serde_json::Value;

pub use parser::Parser; // TODO private
pub use select::JsonPathError;
pub use select::{Selector, SelectorMut};
pub use select::json_node::JsonValueUpdater;
use parser::Node;

// #[doc(hidden)]
// mod ffi;
#[doc(hidden)]
mod ffi;
pub mod parser;
#[doc(hidden)]
mod parser;
#[doc(hidden)]
mod select;
pub mod select;

/// It is a high-order function. it compile a jsonpath and then returns a closure that has JSON as argument. if you need to reuse a jsonpath, it is good for performance.
///
Expand Down Expand Up @@ -174,7 +176,7 @@ mod select;
since = "0.2.5",
note = "Please use the Compiled::compile function instead"
)]
pub fn compile(path: &str) -> impl FnMut(&Value) -> Result<Vec<&Value>, JsonPathError> {
pub fn compile(path: &str) -> impl FnMut(&Value) -> Result<Vec<&Value>, JsonPathError> + '_ {
let node = parser::Parser::compile(path);
move |json| match &node {
Ok(node) => {
Expand Down Expand Up @@ -220,7 +222,7 @@ pub fn compile(path: &str) -> impl FnMut(&Value) -> Result<Vec<&Value>, JsonPath
/// ]);
/// ```
#[allow(clippy::needless_lifetimes)]
pub fn selector<'a>(json: &'a Value) -> impl FnMut(&str) -> Result<Vec<&'a Value>, JsonPathError> {
pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result<Vec<&'a Value>, JsonPathError> {
let mut selector = Selector::default();
let _ = selector.value(json);
move |path: &str| selector.str_path(path)?.reset_value().select()
Expand Down Expand Up @@ -272,12 +274,27 @@ pub fn selector<'a>(json: &'a Value) -> impl FnMut(&str) -> Result<Vec<&'a Value
///
/// assert_eq!(json, ret);
/// ```
pub fn selector_as<T: serde::de::DeserializeOwned>(
json: &Value,
) -> impl FnMut(&str) -> Result<Vec<T>, JsonPathError> + '_ {
pub fn selector_as<'a, T: serde::de::DeserializeOwned>(
json: &'a Value,
) -> impl FnMut(&'a str) -> Result<Vec<T>, JsonPathError> + '_ {
let mut selector = Selector::default();
let _ = selector.value(json);
move |path: &str| selector.str_path(path)?.reset_value().select_as()
move |path: &str| {
let res = selector.str_path(path)?.reset_value().select();
match res {
Ok(vec) => {
let mut ret = Vec::new();
for v in vec {
match T::deserialize(v) {
Ok(v) => ret.push(v),
Err(e) => return Err(JsonPathError::Serde(e.to_string())),
}
}
Ok(ret)
}
_ => Err(JsonPathError::EmptyValue),
}
}
}

/// It is a simple select function. but it compile the jsonpath argument every time.
Expand Down Expand Up @@ -333,56 +350,56 @@ pub fn select<'a>(json: &'a Value, path: &str) -> Result<Vec<&'a Value>, JsonPat
/// assert_eq!(ret, r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#);
/// ```
pub fn select_as_str(json_str: &str, path: &str) -> Result<String, JsonPathError> {
let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
let json: Value = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
let ret = Selector::default().str_path(path)?.value(&json).select()?;
serde_json::to_string(&ret).map_err(|e| JsonPathError::Serde(e.to_string()))
}

/// It is the same to `select` function but it deserialize the the result as given type `T`.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
/// extern crate serde;
/// #[macro_use] extern crate serde_json;
///
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Deserialize, PartialEq, Debug)]
/// struct Person {
/// name: String,
/// age: u8,
/// phones: Vec<String>,
/// }
///
/// let ret: Vec<Person> = jsonpath::select_as(r#"
/// {
/// "person":
/// {
/// "name": "Doe John",
/// "age": 44,
/// "phones": [
/// "+44 1234567",
/// "+44 2345678"
/// ]
/// }
/// }
/// "#, "$.person").unwrap();
///
/// let person = Person {
/// name: "Doe John".to_string(),
/// age: 44,
/// phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()],
/// };
///
/// assert_eq!(ret[0], person);
/// ```
pub fn select_as<T: serde::de::DeserializeOwned>(
json_str: &str,
path: &str,
) -> Result<Vec<T>, JsonPathError> {
let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
Selector::default().str_path(path)?.value(&json).select_as()
}
// /// It is the same to `select` function but it deserialize the the result as given type `T`.
// ///
// /// ```rust
// /// extern crate jsonpath_lib as jsonpath;
// /// extern crate serde;
// /// #[macro_use] extern crate serde_json;
// ///
// /// use serde::{Deserialize, Serialize};
// ///
// /// #[derive(Deserialize, PartialEq, Debug)]
// /// struct Person {
// /// name: String,
// /// age: u8,
// /// phones: Vec<String>,
// /// }
// ///
// /// let ret: Vec<Person> = jsonpath::select_as(r#"
// /// {
// /// "person":
// /// {
// /// "name": "Doe John",
// /// "age": 44,
// /// "phones": [
// /// "+44 1234567",
// /// "+44 2345678"
// /// ]
// /// }
// /// }
// /// "#, "$.person").unwrap();
// ///
// /// let person = Person {
// /// name: "Doe John".to_string(),
// /// age: 44,
// /// phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()],
// /// };
// ///
// /// assert_eq!(ret[0], person);
// /// ```
// pub fn select_as<T: serde::de::DeserializeOwned>(
// json_str: &str,
// path: &str,
// ) -> Result<Vec<T>, JsonPathError> {
// let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
// (selector_as(&json))(path)
// }

/// Delete(= replace with null) the JSON property using the jsonpath.
///
Expand Down Expand Up @@ -416,10 +433,11 @@ pub fn select_as<T: serde::de::DeserializeOwned>(
/// {"name": "친구4"}
/// ]}));
/// ```
pub fn delete(value: Value, path: &str) -> Result<Value, JsonPathError> {
pub fn delete(mut value: Value, path: &str) -> Result<Value, JsonPathError> {
let mut selector = SelectorMut::default();
let value = selector.str_path(path)?.value(value).delete()?;
Ok(value.take().unwrap_or(Value::Null))
let mut updater = JsonValueUpdater::new(|_| Some(Value::Null));
selector.str_path(path)?.value(&mut value).replace_with(&mut updater)?;
Ok(value)
}

/// Select JSON properties using a jsonpath and transform the result and then replace it. via closure that implements `FnMut` you can transform the selected results.
Expand Down Expand Up @@ -464,13 +482,18 @@ pub fn delete(value: Value, path: &str) -> Result<Value, JsonPathError> {
/// {"name": "친구4"}
/// ]}));
/// ```
pub fn replace_with<F>(value: Value, path: &str, fun: &mut F) -> Result<Value, JsonPathError>
pub fn replace_with<'a, F>(mut value: Value, path: &str, fun: F) -> Result<Value, JsonPathError>
where
F: FnMut(Value) -> Option<Value>,
{
let mut selector = SelectorMut::default();
let value = selector.str_path(path)?.value(value).replace_with(fun)?;
Ok(value.take().unwrap_or(Value::Null))
let mut updater = JsonValueUpdater::new(fun);
selector.str_path(path)?.value(&mut value).replace_with(&mut updater)?;
Ok(value)

// let mut selector = SelectorMut::default();
// let value = selector.str_path(path)?.value(value).replace_with(fun)?;
// Ok(value.take().unwrap_or(Value::Null))
}

/// A pre-compiled expression.
Expand Down Expand Up @@ -516,11 +539,11 @@ where
/// ]);
/// ```
#[derive(Clone, Debug)]
pub struct Compiled {
node: Node,
pub struct Compiled<'a> {
node: Node<'a>,
}

impl Compiled {
impl<'a> Compiled<'a> {
/// Compile a path expression and return a compiled instance.
///
/// If parsing the path fails, it will return an error.
Expand All @@ -532,7 +555,7 @@ impl Compiled {
}

/// Execute the select operation on the pre-compiled path.
pub fn select<'a>(&self, value: &'a Value) -> Result<Vec<&'a Value>, JsonPathError> {
pub fn select<'b>(&self, value: &'b Value) -> Result<Vec<&'b Value>, JsonPathError> {
let mut selector = Selector::default();
selector.compiled_path(&self.node).value(value).select()
}
Expand Down
Loading