Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8a1166789
|
||
|
|
82fa955359
|
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "json_dotpath"
|
name = "json_dotpath"
|
||||||
version = "1.0.0"
|
version = "1.0.2"
|
||||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ Five principal methods are added by the `DotPaths` trait to `serde_json::Value`,
|
|||||||
- `dot_get_mut(path)` - get a mutable reference to an element of the JSON object
|
- `dot_get_mut(path)` - get a mutable reference to an element of the JSON object
|
||||||
- `dot_set(path, value)` - set a new value, dropping the original (if any)
|
- `dot_set(path, value)` - set a new value, dropping the original (if any)
|
||||||
- `dot_replace(path, value)` - set a new value, returning the original (if any)
|
- `dot_replace(path, value)` - set a new value, returning the original (if any)
|
||||||
- `dot_take(path, value)` - remove a value by path, returning it (if any)
|
- `dot_take(path)` - remove a value by path, returning it (if any)
|
||||||
|
|
||||||
`dot_set()` supports array manipulation syntax not found in the other methods, namely the
|
`dot_set()` supports array manipulation syntax not found in the other methods, namely the
|
||||||
`>n` and `<n` pattern to insert an element before or after an index, shifting the rest of the `Vec`.
|
`>n` and `<n` pattern to insert an element before or after an index, shifting the rest of the `Vec`.
|
||||||
|
|||||||
+90
-19
@@ -145,14 +145,7 @@ pub trait DotPaths {
|
|||||||
/// - `<` ... first element of an array (same as `0`)
|
/// - `<` ... first element of an array (same as `0`)
|
||||||
fn dot_set<T>(&mut self, path: &str, value: T) -> Result<()>
|
fn dot_set<T>(&mut self, path: &str, value: T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize;
|
||||||
{
|
|
||||||
// This is a default implementation.
|
|
||||||
// Vec uses a custom implementation to support the special syntax.
|
|
||||||
|
|
||||||
let _ = self.dot_replace::<T, Value>(path, value)?; // Original value is dropped
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replace a value by path with a new value.
|
/// Replace a value by path with a new value.
|
||||||
/// The value types do not have to match.
|
/// The value types do not have to match.
|
||||||
@@ -321,6 +314,7 @@ impl DotPaths for serde_json::Value {
|
|||||||
match self {
|
match self {
|
||||||
// Special case for Vec, which implements additional path symbols
|
// Special case for Vec, which implements additional path symbols
|
||||||
Value::Array(a) => a.dot_set(path, value),
|
Value::Array(a) => a.dot_set(path, value),
|
||||||
|
Value::Object(m) => m.dot_set(path, value),
|
||||||
_ => {
|
_ => {
|
||||||
let _ = self.dot_replace::<T, Value>(path, value)?; // Original value is dropped
|
let _ = self.dot_replace::<T, Value>(path, value)?; // Original value is dropped
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -412,6 +406,29 @@ impl DotPaths for serde_json::Map<String, serde_json::Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dot_set<T>(&mut self, path: &str, value: T) -> Result<()> where
|
||||||
|
T: Serialize {
|
||||||
|
let (my, sub) = path_split(path);
|
||||||
|
|
||||||
|
if my.is_empty() {
|
||||||
|
return Err(InvalidKey(my));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(subpath) = sub {
|
||||||
|
if self.contains_key(&my) {
|
||||||
|
self.get_mut(&my).unwrap().dot_set(subpath, value)
|
||||||
|
} else {
|
||||||
|
// Build new subpath
|
||||||
|
let _ = self.insert(my, new_by_path_root(subpath, value)?); // always returns None here
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let packed = serde_json::to_value(value)?;
|
||||||
|
self.insert(my, packed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn dot_replace<NEW, OLD>(&mut self, path: &str, value: NEW) -> Result<Option<OLD>>
|
fn dot_replace<NEW, OLD>(&mut self, path: &str, value: NEW) -> Result<Option<OLD>>
|
||||||
where
|
where
|
||||||
NEW: Serialize,
|
NEW: Serialize,
|
||||||
@@ -425,10 +442,7 @@ impl DotPaths for serde_json::Map<String, serde_json::Value> {
|
|||||||
|
|
||||||
if let Some(subpath) = sub {
|
if let Some(subpath) = sub {
|
||||||
if self.contains_key(&my) {
|
if self.contains_key(&my) {
|
||||||
match self.get_mut(&my) {
|
self.get_mut(&my).unwrap().dot_replace(subpath, value)
|
||||||
None => Ok(None),
|
|
||||||
Some(m) => m.dot_replace(subpath, value),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Build new subpath
|
// Build new subpath
|
||||||
let _ = self.insert(my, new_by_path_root(subpath, value)?); // always returns None here
|
let _ = self.insert(my, new_by_path_root(subpath, value)?); // always returns None here
|
||||||
@@ -487,7 +501,9 @@ impl DotPaths for Vec<serde_json::Value> {
|
|||||||
let index: usize = match my.as_str() {
|
let index: usize = match my.as_str() {
|
||||||
">" => self.len() - 1, // non-empty checked above
|
">" => self.len() - 1, // non-empty checked above
|
||||||
"<" => 0,
|
"<" => 0,
|
||||||
_ => my.parse().map_err(|_| InvalidKey(my))?,
|
_ => my.parse().map_err(|_| {
|
||||||
|
InvalidKey(my)
|
||||||
|
})?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if index >= self.len() {
|
if index >= self.len() {
|
||||||
@@ -524,7 +540,9 @@ impl DotPaths for Vec<serde_json::Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"<" => 0,
|
"<" => 0,
|
||||||
_ => my.parse().map_err(|_| InvalidKey(my))?,
|
_ => my.parse().map_err(|_| {
|
||||||
|
InvalidKey(my)
|
||||||
|
})?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if index > self.len() {
|
if index > self.len() {
|
||||||
@@ -593,14 +611,23 @@ impl DotPaths for Vec<serde_json::Value> {
|
|||||||
_ if my.starts_with('>') => {
|
_ if my.starts_with('>') => {
|
||||||
// insert after
|
// insert after
|
||||||
insert = true;
|
insert = true;
|
||||||
(&my[1..]).parse::<usize>().map_err(|_| InvalidKey(my_s))? + 1
|
(&my[1..]).parse::<usize>()
|
||||||
|
.map_err(|_| {
|
||||||
|
InvalidKey(my_s)
|
||||||
|
})? + 1
|
||||||
}
|
}
|
||||||
_ if my.starts_with('<') => {
|
_ if my.starts_with('<') => {
|
||||||
// insert before
|
// insert before
|
||||||
insert = true;
|
insert = true;
|
||||||
(&my[1..]).parse::<usize>().map_err(|_| InvalidKey(my_s))?
|
(&my[1..]).parse::<usize>()
|
||||||
|
.map_err(|_| {
|
||||||
|
InvalidKey(my_s)
|
||||||
|
})?
|
||||||
}
|
}
|
||||||
_ => my.parse::<usize>().map_err(|_| InvalidKey(my_s))?,
|
_ => my.parse::<usize>()
|
||||||
|
.map_err(|_| {
|
||||||
|
InvalidKey(my_s)
|
||||||
|
})?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if index > self.len() {
|
if index > self.len() {
|
||||||
@@ -657,7 +684,9 @@ impl DotPaths for Vec<serde_json::Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"<" => 0,
|
"<" => 0,
|
||||||
_ => my.parse().map_err(|_| InvalidKey(my))?,
|
_ => my.parse().map_err(|_| {
|
||||||
|
InvalidKey(my)
|
||||||
|
})?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if index >= self.len() {
|
if index >= self.len() {
|
||||||
@@ -700,7 +729,9 @@ impl DotPaths for Vec<serde_json::Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"<" => 0,
|
"<" => 0,
|
||||||
_ => my.parse().map_err(|_| InvalidKey(my))?,
|
_ => my.parse().map_err(|_| {
|
||||||
|
InvalidKey(my)
|
||||||
|
})?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if index >= self.len() {
|
if index >= self.len() {
|
||||||
@@ -838,6 +869,46 @@ mod tests {
|
|||||||
assert_eq!(json!([[["first"]], [["second"]]]), vec);
|
assert_eq!(json!([[["first"]], [["second"]]]), vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_append1() {
|
||||||
|
let mut test_array = Value::Array(vec![]);
|
||||||
|
test_array.dot_set(">>", Value::String(String::from("Go to class"))).unwrap();
|
||||||
|
test_array.dot_set(">>", Value::String(String::from("Fish"))).unwrap();
|
||||||
|
assert_eq!(json!(["Go to class","Fish"]), test_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_append2() {
|
||||||
|
let mut test_array = Value::Array(vec![]);
|
||||||
|
test_array.dot_set("+", Value::String(String::from("Go to class"))).unwrap();
|
||||||
|
test_array.dot_set("+", Value::String(String::from("Fish"))).unwrap();
|
||||||
|
assert_eq!(json!(["Go to class","Fish"]), test_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_append_in_object1() {
|
||||||
|
let mut test_inner_array = Value::Null;
|
||||||
|
|
||||||
|
test_inner_array.dot_set("todos.+", Value::String(String::from("Go to class"))).unwrap();
|
||||||
|
assert_eq!(json!({"todos" : ["Go to class"] }), test_inner_array);
|
||||||
|
|
||||||
|
test_inner_array.dot_set("todos.+", Value::String(String::from("Fish"))).unwrap();
|
||||||
|
assert_eq!(json!({"todos" : ["Go to class","Fish"] }), test_inner_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_append_in_object2() {
|
||||||
|
let mut test_inner_array = Value::Null;
|
||||||
|
test_inner_array.dot_set("name", Value::String(String::from("Google"))).unwrap();
|
||||||
|
assert_eq!(json!({"name" : "Google"}), test_inner_array);
|
||||||
|
|
||||||
|
test_inner_array.dot_set("todos.+", Value::String(String::from("Go to class"))).unwrap();
|
||||||
|
assert_eq!(json!({"name" : "Google", "todos" : ["Go to class"] }), test_inner_array);
|
||||||
|
|
||||||
|
test_inner_array.dot_set("todos.+", Value::String(String::from("Fish"))).unwrap();
|
||||||
|
assert_eq!(json!({"name" : "Google", "todos" : ["Go to class","Fish"] }), test_inner_array);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_vec() {
|
fn get_vec() {
|
||||||
let vec = json!([
|
let vec = json!([
|
||||||
|
|||||||
Reference in New Issue
Block a user