Skip to content
This repository was archived by the owner on Nov 14, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 51 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,57 @@ where
Ok(value.take().unwrap_or(Value::Null))
}

/// Select JSON properties using a jsonpath, exposing the path to the values with the value itself. Allows updating of the value while tracking field names/paths.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
/// #[macro_use] extern crate serde_json;
///
/// use serde_json::Value;
///
/// let json_obj = json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]});
///
/// let ret = jsonpath::replace_with_tokens(json_obj, "$..[?(@.age == 20)].age", &mut |v, _tokens| {
/// let age = if let Value::Number(n) = v {
/// n.as_u64().unwrap() * 2
/// } else {
/// 0
/// };
///
/// Ok(Some(json!(age)))
/// }).unwrap();
///
/// assert_eq!(ret, json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 40},
/// {"name": "친구2", "age": 40}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]}));
/// ```
pub fn replace_with_tokens<F>(value: Value, path: &str, fun: &mut F) -> Result<Value, JsonPathError>
where
F: FnMut(Value, &[String]) -> Result<Option<Value>, JsonPathError>,
{
let mut selector = SelectorMut::default();
let value = selector.str_path(path)?.value(value).replace_with_tokens(fun)?;
Ok(value.take().unwrap_or(Value::Null))
}

/// A pre-compiled expression.
///
/// Calling the select function of this struct will re-use the existing, compiled expression.
Expand Down
75 changes: 74 additions & 1 deletion src/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,61 @@ pub struct SelectorMut {
value: Option<Value>,
}

fn replace_value_with_tokens<F: FnMut(Value, &[String]) -> Result<Option<Value>, JsonPathError>>(
mut tokens: Vec<String>,
value: &mut Value,
fun: &mut F,
) -> Result<(), JsonPathError> {
let mut target = value;

let tokens_clone = tokens.clone();
let last_index = tokens.len().saturating_sub(1);
for (i, token) in tokens.drain(..).enumerate() {
let target_once = target;
let is_last = i == last_index;
let target_opt = match *target_once {
Value::Object(ref mut map) => {
if is_last {
if let Entry::Occupied(mut e) = map.entry(token) {
let v = e.insert(Value::Null);
if let Some(res) = fun(v,&tokens_clone)? {
e.insert(res);
} else {
e.remove();
}
}
return Ok(());
}
map.get_mut(&token)
}
Value::Array(ref mut vec) => {
if let Ok(x) = token.parse::<usize>() {
if is_last {
let v = std::mem::replace(&mut vec[x], Value::Null);
if let Some(res) = fun(v,&tokens_clone)? {
vec[x] = res;
} else {
vec.remove(x);
}
return Ok(());
}
vec.get_mut(x)
} else {
None
}
}
_ => None,
};

if let Some(t) = target_opt {
target = t;
} else {
break;
}
}
Ok(())
}

fn replace_value<F: FnMut(Value) -> Result<Option<Value>, JsonPathError>>(
mut tokens: Vec<String>,
value: &mut Value,
Expand Down Expand Up @@ -962,6 +1017,24 @@ impl SelectorMut {

Ok(self)
}

pub fn replace_with_tokens<F: FnMut(Value, &[String]) -> Result<Option<Value>, JsonPathError>>(
&mut self,
fun: &mut F
) -> Result<&mut Self, JsonPathError> {
let paths = {
let result = self.select()?;
self.compute_paths(result)
};

if let Some(ref mut value) = &mut self.value {
for tokens in paths {
replace_value_with_tokens(tokens, value, fun)?;
}
}

Ok(self)
}
}


Expand Down Expand Up @@ -1001,4 +1074,4 @@ mod select_inner_tests {
panic!();
}
}
}
}