mirror of
https://github.com/fluencelabs/jsonpath
synced 2025-04-25 09:22:19 +00:00
SelectorMut first commit
This commit is contained in:
parent
9a08df7843
commit
dd9315bc90
@ -10,6 +10,8 @@ use std::io::Read;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use jsonpath::{SelectorMut, Selector};
|
||||
|
||||
use self::test::Bencher;
|
||||
|
||||
fn read_json(path: &str) -> String {
|
||||
@ -102,4 +104,33 @@ fn bench_select_as(b: &mut Bencher) {
|
||||
let _: Vec<Book> = jsonpath::select_as(&json, r#"$..book[?(@.price<30 && @.category=="fiction")][0]"#).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_delete(b: &mut Bencher) {
|
||||
let json = get_json();
|
||||
let mut selector = SelectorMut::new();
|
||||
let _ = selector.str_path(get_path());
|
||||
|
||||
b.iter(move || {
|
||||
for _ in 1..100 {
|
||||
let _ = selector.value(json.clone()).delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_select_to_compare_with_delete(b: &mut Bencher) {
|
||||
let json = &get_json();
|
||||
|
||||
let mut selector = Selector::new();
|
||||
let _ = selector.str_path(get_path());
|
||||
let _ = selector.value(json);
|
||||
|
||||
b.iter(move || {
|
||||
for _ in 1..100 {
|
||||
let _ = json.clone();
|
||||
let _ = selector.reset_value().select();
|
||||
}
|
||||
});
|
||||
}
|
@ -55,7 +55,7 @@ fn _selector(b: &mut Bencher, index: usize) {
|
||||
b.iter(move || {
|
||||
for _ in 1..100 {
|
||||
let mut selector = jsonpath::Selector::new();
|
||||
let _ = selector.path(get_path(index));
|
||||
let _ = selector.str_path(get_path(index));
|
||||
selector.value(&json);
|
||||
let r = selector.select();
|
||||
if r.is_err() {
|
||||
|
20
src/lib.rs
20
src/lib.rs
@ -125,23 +125,23 @@
|
||||
extern crate array_tool;
|
||||
extern crate core;
|
||||
extern crate env_logger;
|
||||
extern crate indexmap;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate indexmap;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
pub use parser::parser::{Node, Parser};
|
||||
pub use select::{Selector, SelectorMut};
|
||||
pub use select::JsonPathError;
|
||||
|
||||
#[doc(hidden)]
|
||||
mod parser;
|
||||
#[doc(hidden)]
|
||||
mod select;
|
||||
|
||||
pub use select::Selector;
|
||||
pub use select::JsonPathError;
|
||||
pub use parser::parser::{Node, Parser};
|
||||
|
||||
/// It is a high-order function. it compile a JsonPath and then returns a function. this return-function can be reused for different JsonObjects.
|
||||
///
|
||||
/// ```rust
|
||||
@ -219,7 +219,7 @@ pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result<Vec<&Value
|
||||
let mut selector = Selector::new();
|
||||
let _ = selector.value(json);
|
||||
move |path: &str| {
|
||||
selector.path(path)?.reset_value().select()
|
||||
selector.str_path(path)?.reset_value().select()
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +273,7 @@ pub fn selector_as<T: serde::de::DeserializeOwned>(json: &Value) -> impl FnMut(&
|
||||
let mut selector = Selector::new();
|
||||
let _ = selector.value(json);
|
||||
move |path: &str| {
|
||||
selector.path(path)?.reset_value().select_as()
|
||||
selector.str_path(path)?.reset_value().select_as()
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ pub fn selector_as<T: serde::de::DeserializeOwned>(json: &Value) -> impl FnMut(&
|
||||
/// ]);
|
||||
/// ```
|
||||
pub fn select<'a>(json: &'a Value, path: &'a str) -> Result<Vec<&'a Value>, JsonPathError> {
|
||||
Selector::new().path(path)?.value(json).select()
|
||||
Selector::new().str_path(path)?.value(json).select()
|
||||
}
|
||||
|
||||
/// This function compile a jsonpath everytime and it convert `&str` to `jsonpath's RefValue` everytime and then it return a json string.
|
||||
@ -331,7 +331,7 @@ pub fn select<'a>(json: &'a Value, path: &'a str) -> Result<Vec<&'a Value>, Json
|
||||
/// ```
|
||||
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 ret = Selector::new().path(path)?.value(&json).select()?;
|
||||
let ret = Selector::new().str_path(path)?.value(&json).select()?;
|
||||
serde_json::to_string(&ret).map_err(|e| JsonPathError::Serde(e.to_string()))
|
||||
}
|
||||
|
||||
@ -375,5 +375,5 @@ pub fn select_as_str(json_str: &str, path: &str) -> Result<String, JsonPathError
|
||||
/// ```
|
||||
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::new().path(path)?.value(&json).select_as()
|
||||
Selector::new().str_path(path)?.value(&json).select_as()
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use array_tool::vec::{Intersect, Union};
|
||||
use serde_json::{Number, Value};
|
||||
|
||||
use parser::parser::*;
|
||||
use std::collections::HashSet;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
fn to_f64(n: &Number) -> f64 {
|
||||
if n.is_i64() {
|
||||
@ -382,6 +384,21 @@ impl<'a> ExprTerm<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<ExprTerm<'a>> for &Vec<&'a Value> {
|
||||
fn into(self) -> ExprTerm<'a> {
|
||||
if self.len() == 1 {
|
||||
match &self[0] {
|
||||
Value::Number(v) => return ExprTerm::Number(v.clone()),
|
||||
Value::String(v) => return ExprTerm::String(v.clone()),
|
||||
Value::Bool(v) => return ExprTerm::Bool(*v),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
ExprTerm::Json(None, self.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_all_with_str<'a>(vec: &Vec<&'a Value>, tmp: &mut Vec<&'a Value>, key: &str, is_filter: bool) {
|
||||
if is_filter {
|
||||
walk(vec, tmp, &|v| match v {
|
||||
@ -491,19 +508,19 @@ impl<'a> Selector<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> {
|
||||
pub fn str_path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> {
|
||||
debug!("path : {}", path);
|
||||
self.node = Some(Parser::compile(path).map_err(|e| JsonPathError::Path(e))?);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub(crate) fn reset_value(&mut self) -> &mut Self {
|
||||
self.current = None;
|
||||
pub fn compiled_path(&mut self, node: Node) -> &mut Self {
|
||||
self.node = Some(node);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn compiled_path(&mut self, node: Node) -> &mut Self {
|
||||
self.node = Some(node);
|
||||
pub fn reset_value(&mut self) -> &mut Self {
|
||||
self.current = None;
|
||||
self
|
||||
}
|
||||
|
||||
@ -733,19 +750,10 @@ impl<'a> NodeVisitor for Selector<'a> {
|
||||
if !self.selectors.is_empty() {
|
||||
match token {
|
||||
ParseToken::Absolute | ParseToken::Relative | ParseToken::Filter(_) => {
|
||||
let s = self.selectors.pop().unwrap();
|
||||
let selector = self.selectors.pop().unwrap();
|
||||
|
||||
if let Some(current) = &s.current {
|
||||
let term = if current.len() == 1 {
|
||||
match current[0] {
|
||||
Value::Number(v) => ExprTerm::Number(v.clone()),
|
||||
Value::String(v) => ExprTerm::String(v.clone()),
|
||||
Value::Bool(v) => ExprTerm::Bool(*v),
|
||||
_ => ExprTerm::Json(None, current.to_vec())
|
||||
}
|
||||
} else {
|
||||
ExprTerm::Json(None, current.to_vec())
|
||||
};
|
||||
if let Some(current) = &selector.current {
|
||||
let term = current.into();
|
||||
|
||||
if let Some(s) = self.selectors.last_mut() {
|
||||
s.terms.push(Some(term));
|
||||
@ -760,19 +768,20 @@ impl<'a> NodeVisitor for Selector<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(s) = self.selectors.last_mut() {
|
||||
s.visit_token(token);
|
||||
if let Some(selector) = self.selectors.last_mut() {
|
||||
selector.visit_token(token);
|
||||
return;
|
||||
}
|
||||
|
||||
match token {
|
||||
ParseToken::Absolute => {
|
||||
if self.current.is_some() {
|
||||
let mut s = Selector::new();
|
||||
let mut selector = Selector::new();
|
||||
|
||||
if let Some(value) = self.value {
|
||||
s.value = Some(value);
|
||||
s.current = Some(vec![value]);
|
||||
self.selectors.push(s);
|
||||
selector.value = Some(value);
|
||||
selector.current = Some(vec![value]);
|
||||
self.selectors.push(selector);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -964,15 +973,143 @@ impl<'a> NodeVisitor for Selector<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Modifiable {
|
||||
fn delete_from_selected(&mut self, value: &mut Value);
|
||||
pub struct SelectorMut {
|
||||
path: Option<Node>,
|
||||
value: Option<Value>,
|
||||
}
|
||||
|
||||
impl<'a> Modifiable for Selector<'a> {
|
||||
fn delete_from_selected(&mut self, value: &mut Value) {
|
||||
match &self.current {
|
||||
Some(current) => {}
|
||||
_ => {}
|
||||
impl SelectorMut {
|
||||
pub fn new() -> Self {
|
||||
SelectorMut { path: None, value: None }
|
||||
}
|
||||
|
||||
pub fn str_path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> {
|
||||
self.path = Some(Parser::compile(path).map_err(|e| JsonPathError::Path(e))?);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn value(&mut self, value: Value) -> &mut Self {
|
||||
self.value = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> Option<Value> {
|
||||
self.value.take()
|
||||
}
|
||||
|
||||
fn compute_paths(&self, result: &Vec<&Value>) -> Vec<Vec<String>> {
|
||||
fn _walk(origin: &Value, target: &Value, tokens: &mut Vec<String>, visited: &mut IndexSet<Vec<String>>) -> bool {
|
||||
if visited.contains(tokens) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if std::ptr::eq(origin, target) {
|
||||
debug!("tokens: {:?}", tokens);
|
||||
return true;
|
||||
}
|
||||
|
||||
match origin {
|
||||
Value::Array(vec) => for (i, v) in vec.iter().enumerate() {
|
||||
tokens.push(i.to_string());
|
||||
if _walk(v, target, tokens, visited) {
|
||||
return true;
|
||||
}
|
||||
tokens.pop();
|
||||
},
|
||||
Value::Object(map) => for (k, v) in map {
|
||||
tokens.push(k.clone());
|
||||
if _walk(v, target, tokens, visited) {
|
||||
return true;
|
||||
}
|
||||
tokens.pop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut visited = IndexSet::new();
|
||||
|
||||
if let Some(origin) = &self.value {
|
||||
for v in result {
|
||||
let mut tokens = Vec::new();
|
||||
if _walk(origin, v, &mut tokens, &mut visited) {
|
||||
visited.insert(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visited.iter().map(|v| v.to_vec()).collect()
|
||||
}
|
||||
|
||||
pub fn delete(&mut self) -> Result<&mut Self, JsonPathError> {
|
||||
self.replace_with(&mut |_| Value::Null)
|
||||
}
|
||||
|
||||
pub fn replace_with<F: FnMut(&Value) -> Value>(&mut self, fun: &mut F) -> Result<&mut Self, JsonPathError> {
|
||||
let mut selector = Selector::new();
|
||||
|
||||
if let Some(path) = self.path.take() {
|
||||
selector.compiled_path(path);
|
||||
}
|
||||
|
||||
if let Some(value) = &self.value {
|
||||
selector.value(value);
|
||||
}
|
||||
|
||||
let result = selector.select();
|
||||
|
||||
self.path = Some(selector.node.unwrap());
|
||||
|
||||
let paths = self.compute_paths(&result?);
|
||||
|
||||
if let Some(mut value) = self.value.take() {
|
||||
for tokens in paths {
|
||||
self.replace_value(tokens, &mut value, fun);
|
||||
}
|
||||
self.value = Some(value);
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn replace_value<F: FnMut(&Value) -> Value>(&mut self, tokens: Vec<String>, value: &mut Value, fun: &mut F) {
|
||||
let mut target = value;
|
||||
|
||||
for (i, token) in tokens.iter().enumerate() {
|
||||
let target_once = target;
|
||||
let is_last = i == tokens.len() - 1;
|
||||
let target_opt = match *target_once {
|
||||
Value::Object(ref mut map) => {
|
||||
if is_last {
|
||||
if let Some(v) = map.remove(token) {
|
||||
map.insert(token.clone(), fun(&v));
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.get_mut(token)
|
||||
}
|
||||
Value::Array(ref mut vec) => {
|
||||
if let Ok(x) = token.parse::<usize>() {
|
||||
if is_last {
|
||||
let v = &vec[x];
|
||||
vec[x] = fun(v);
|
||||
return;
|
||||
}
|
||||
vec.get_mut(x)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(t) = target_opt {
|
||||
target = t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ pub fn read_contents(path: &str) -> String {
|
||||
#[allow(dead_code)]
|
||||
pub fn select_and_then_compare<'a>(path: &str, json: Value, target: Value) {
|
||||
let mut s = Selector::new();
|
||||
let _ = s.path(path);
|
||||
let _ = s.str_path(path);
|
||||
let _ = s.value(&json);
|
||||
let result = serde_json::to_value(s.select().unwrap()).unwrap();
|
||||
assert_eq!(result, target, "{}", path);
|
||||
|
40
tests/mutable.rs
Normal file
40
tests/mutable.rs
Normal file
@ -0,0 +1,40 @@
|
||||
extern crate jsonpath_lib as jsonpath;
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
|
||||
use common::{read_json, setup};
|
||||
use jsonpath::{SelectorMut, Selector};
|
||||
use serde_json::Value;
|
||||
|
||||
mod common;
|
||||
|
||||
#[test]
|
||||
fn selector_mut() {
|
||||
setup();
|
||||
|
||||
let mut selector_mut = SelectorMut::new();
|
||||
|
||||
let mut nums = Vec::new();
|
||||
let result = selector_mut
|
||||
.str_path(r#"$.store..price"#).unwrap()
|
||||
.value(read_json("./benches/example.json"))
|
||||
.replace_with(&mut |v| {
|
||||
match v {
|
||||
Value::Number(n) => {
|
||||
nums.push(n.as_f64().unwrap());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Value::String("a".to_string())
|
||||
}).unwrap()
|
||||
.take().unwrap();
|
||||
|
||||
assert_eq!(nums, vec![8.95_f64, 12.99_f64, 8.99_f64, 22.99_f64, 19.95_f64]);
|
||||
|
||||
let mut selector = Selector::new();
|
||||
let result = selector.str_path(r#"$.store..price"#).unwrap()
|
||||
.value(&result)
|
||||
.select().unwrap();
|
||||
|
||||
assert_eq!(vec![&json!("a"), &json!("a"), &json!("a"), &json!("a"), &json!("a")], result);
|
||||
}
|
@ -151,7 +151,7 @@ fn readme_selector() {
|
||||
let mut selector = Selector::new();
|
||||
|
||||
let result = selector
|
||||
.path("$..[?(@.age >= 30)]").unwrap()
|
||||
.str_path("$..[?(@.age >= 30)]").unwrap()
|
||||
.value(&json_obj)
|
||||
.select().unwrap();
|
||||
|
||||
|
@ -108,7 +108,7 @@ pub fn selector(js_value: JsValue) -> JsValue {
|
||||
#[wasm_bindgen]
|
||||
pub fn select(js_value: JsValue, path: &str) -> JsValue {
|
||||
let mut selector = _Selector::new();
|
||||
let _ = selector.path(path);
|
||||
let _ = selector.str_path(path);
|
||||
|
||||
let json = match into_serde_json(&js_value) {
|
||||
Ok(json) => json,
|
||||
@ -160,7 +160,7 @@ impl Selector {
|
||||
let mut selector = _Selector::new();
|
||||
|
||||
if let Some(path) = &self.path {
|
||||
let _ = selector.path(&path).map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
|
||||
let _ = selector.str_path(&path).map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
|
||||
} else {
|
||||
return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyPath)));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user