Hi,
So recently I've finished developing a project where the main routine was to update nested values from a variety JSON files.
This initial implementation I was able to build with a Python library named glom. From it I'm only using the "read" and "update" routines for project.
Now that we reached a fairly stable version I though that would be great chance to try introduce Rust on project but the main problem here is that I was not able to find anything like "glom" for Rust.
So after a quick first round of experimentation I was able to come up with this "read" clone that supports retrieving nested values for object or list:
use serde_json::Value;
use regex::Regex;
pub fn json_read<'a>(path: &str, data: &'a Value) -> Option<&'a Value> {
let tokens = path.split(".").collect::<Vec<&str>>();
let re_vec_idx = Regex::new(r"^\[(\d+)\]$").unwrap();
let mut sel_data = Some(data);
for token in tokens {
let vec_idx = match re_vec_idx.captures(token) {
Some(cap) => Some(cap[1].parse::<usize>().unwrap()),
_ => None,
};
sel_data = match sel_data {
Some(value) => match vec_idx {
Some(idx) => value.get(idx),
None => value.get(token),
},
None => None,
};
}
return sel_data;
}
and can be used like:
#[test]
fn read_inner_key_json() {
let json_str = r#"
{
"foo": {
"bar": "bingo!"
}
}
"#;
let json_data: Result<Value> = serde_json::from_str(json_str);
assert_eq!(json_data.is_ok(), true);
let val: Option<&Value> = json_read(
"foo.bar",
json_data.as_ref().unwrap()
);
assert_eq!(val.is_none(), false);
assert_eq!(val.unwrap(), "bingo!");
}
But the tricky parts for me is coming up with the "update" routine. The initial tests I designed was:
#[test]
fn update_inner_key_json() {
let json_str = r#"
{
"foo": {
"bar": "bingo!"
}
}
"#;
let json_data: Result<Value> = serde_json::from_str(json_str);
assert_eq!(json_data.is_ok(), true);
let old_val: Option<&Value> = json_update(
&mut json_data,
"foo.bar",
"updated!"
);
assert_eq!(old_val.is_none(), false);
assert_eq!(old_val.unwrap(), "bingo!");
let new_val: Option<&Value> = json_read(
"foo.bar",
json_data.as_ref().unwrap()
);
assert_eq!(new_val.is_none(), false);
assert_eq!(new_val.unwrap(), "updated!");
}
So far the this is what I got for the actual implementation for the "update":
pub fn json_update<'a>(data: &'a mut Value, path: &str, new_value: Value) -> Option<&'a Value> {
let mut tokens = path.split(".").peekable();
let mut sel_data = Some(data);
while let Some(token) = tokens.next() {
if tokens.peek().is_none() {
// last token
return sel_data.unwrap()
.as_object()
.as_mut()
.unwrap()
.insert(
token.to_string(),
new_value,
);
}
sel_data = sel_data.unwrap().get(token).as_mut();
}
Does the community have any tips and direction to help me finish this "update" routine?
Thanks for reading