use serde_json::Value;
pub fn get_nested_value<'b>(data: &'b Value, path: &str) -> Option<&'b Value> {
if path.is_empty() {
return Some(data);
}
let parts: Vec<&str> = path.split('.').collect();
let mut current = data;
for part in parts {
match current {
Value::Object(map) => {
let field_name = if let Some(stripped) = part.strip_prefix('#') {
stripped } else {
part
};
current = map.get(field_name)?;
}
Value::Array(arr) => {
let index = match part.parse::<usize>() {
Ok(idx) => idx,
Err(_) => return None, };
if index >= arr.len() {
return None; }
current = arr.get(index)?;
}
_ => return None, }
}
Some(current)
}
pub fn set_nested_value(data: &mut Value, path: &str, value: Value) {
let parts: Vec<&str> = path.split('.').collect();
let mut current = data;
for (i, part) in parts.iter().enumerate() {
if i == parts.len() - 1 {
match current {
Value::Object(map) => {
let field_name = if let Some(stripped) = part.strip_prefix('#') {
stripped } else {
part
};
map.insert(field_name.to_string(), value);
}
Value::Array(arr) => {
if let Ok(index) = part.parse::<usize>() {
while arr.len() <= index {
arr.push(Value::Null);
}
if index < arr.len() {
arr[index] = value;
}
}
}
_ => {}
}
return;
}
let next_is_array = parts
.get(i + 1)
.and_then(|p| p.parse::<usize>().ok())
.is_some();
match current {
Value::Object(map) => {
let field_name = if let Some(stripped) = part.strip_prefix('#') {
stripped } else {
if let Ok(_index) = part.parse::<usize>() {
return;
}
part
};
current = map.entry(field_name.to_string()).or_insert_with(|| {
if next_is_array {
Value::Array(Vec::new())
} else {
Value::Object(serde_json::Map::new())
}
});
}
Value::Array(arr) => {
if let Ok(index) = part.parse::<usize>() {
while arr.len() <= index {
arr.push(Value::Null);
}
if arr[index].is_null() {
arr[index] = if next_is_array {
Value::Array(Vec::new())
} else {
Value::Object(serde_json::Map::new())
};
}
current = &mut arr[index];
} else {
return;
}
}
_ => {
return;
}
}
}
}
pub fn get_nested_value_cloned(data: &Value, path: &str) -> Option<Value> {
get_nested_value(data, path).cloned()
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_get_nested_value() {
let data = json!({
"user": {
"name": "John",
"age": 30,
"addresses": [
{"city": "New York", "zip": "10001"},
{"city": "San Francisco", "zip": "94102"}
],
"preferences": {
"theme": "dark",
"notifications": true
}
},
"items": [1, 2, 3]
});
assert_eq!(get_nested_value(&data, "user.name"), Some(&json!("John")));
assert_eq!(get_nested_value(&data, "user.age"), Some(&json!(30)));
assert_eq!(
get_nested_value(&data, "user.preferences.theme"),
Some(&json!("dark"))
);
assert_eq!(
get_nested_value(&data, "user.preferences.notifications"),
Some(&json!(true))
);
assert_eq!(get_nested_value(&data, "items.0"), Some(&json!(1)));
assert_eq!(get_nested_value(&data, "items.2"), Some(&json!(3)));
assert_eq!(
get_nested_value(&data, "user.addresses.0.city"),
Some(&json!("New York"))
);
assert_eq!(
get_nested_value(&data, "user.addresses.1.zip"),
Some(&json!("94102"))
);
assert_eq!(get_nested_value(&data, "user.missing"), None);
assert_eq!(get_nested_value(&data, "items.10"), None);
assert_eq!(get_nested_value(&data, "user.addresses.2.city"), None);
assert_eq!(get_nested_value(&data, "nonexistent.path"), None);
}
#[test]
fn test_set_nested_value() {
let mut data = json!({});
set_nested_value(&mut data, "name", json!("Alice"));
assert_eq!(data, json!({"name": "Alice"}));
set_nested_value(&mut data, "user.email", json!("alice@example.com"));
assert_eq!(
data,
json!({
"name": "Alice",
"user": {"email": "alice@example.com"}
})
);
set_nested_value(&mut data, "name", json!("Bob"));
assert_eq!(
data,
json!({
"name": "Bob",
"user": {"email": "alice@example.com"}
})
);
set_nested_value(&mut data, "settings.theme.mode", json!("dark"));
assert_eq!(data["settings"]["theme"]["mode"], json!("dark"));
set_nested_value(&mut data, "user.age", json!(25));
assert_eq!(data["user"]["age"], json!(25));
assert_eq!(data["user"]["email"], json!("alice@example.com"));
}
#[test]
fn test_set_nested_value_with_arrays() {
let mut data = json!({
"items": [1, 2, 3]
});
set_nested_value(&mut data, "items.0", json!(10));
assert_eq!(data["items"], json!([10, 2, 3]));
set_nested_value(&mut data, "items.5", json!(50));
assert_eq!(data["items"], json!([10, 2, 3, null, null, 50]));
let mut data2 = json!({});
set_nested_value(&mut data2, "matrix.0.0", json!(1));
set_nested_value(&mut data2, "matrix.0.1", json!(2));
set_nested_value(&mut data2, "matrix.1.0", json!(3));
assert_eq!(
data2,
json!({
"matrix": [[1, 2], [3]]
})
);
}
#[test]
fn test_set_nested_value_array_expansion() {
let mut data = json!({});
set_nested_value(&mut data, "array.2", json!("value"));
assert_eq!(
data,
json!({
"array": [null, null, "value"]
})
);
let mut data2 = json!({});
set_nested_value(&mut data2, "deep.nested.0.field", json!("test"));
assert_eq!(
data2,
json!({
"deep": {
"nested": [{"field": "test"}]
}
})
);
}
#[test]
fn test_get_nested_value_cloned() {
let data = json!({
"user": {
"profile": {
"name": "Alice",
"settings": {"theme": "dark"}
}
}
});
let cloned = get_nested_value_cloned(&data, "user.profile.name");
assert_eq!(cloned, Some(json!("Alice")));
let cloned = get_nested_value_cloned(&data, "user.profile.settings");
assert_eq!(cloned, Some(json!({"theme": "dark"})));
let cloned = get_nested_value_cloned(&data, "user.missing");
assert_eq!(cloned, None);
}
#[test]
fn test_get_nested_value_bounds_checking() {
let data = json!({
"items": [1, 2, 3],
"nested": {
"array": [
{"id": 1},
{"id": 2}
]
}
});
assert_eq!(get_nested_value(&data, "items.0"), Some(&json!(1)));
assert_eq!(get_nested_value(&data, "items.2"), Some(&json!(3)));
assert_eq!(get_nested_value(&data, "items.10"), None);
assert_eq!(get_nested_value(&data, "items.999999"), None);
assert_eq!(get_nested_value(&data, "items.abc"), None);
assert_eq!(get_nested_value(&data, "items.-1"), None);
assert_eq!(get_nested_value(&data, "items.2.5"), None);
assert_eq!(
get_nested_value(&data, "nested.array.0.id"),
Some(&json!(1))
);
assert_eq!(get_nested_value(&data, "nested.array.5.id"), None);
assert_eq!(get_nested_value(&data, ""), Some(&data));
}
#[test]
fn test_set_nested_value_bounds_safety() {
let mut data = json!({});
set_nested_value(&mut data, "large.10", json!("value"));
assert_eq!(data["large"].as_array().unwrap().len(), 11);
assert_eq!(data["large"][10], json!("value"));
for i in 0..10 {
assert_eq!(data["large"][i], json!(null));
}
let mut data2 = json!({"matrix": []});
set_nested_value(&mut data2, "matrix.2.1", json!(5));
assert_eq!(data2["matrix"][0], json!(null));
assert_eq!(data2["matrix"][1], json!(null));
assert_eq!(data2["matrix"][2][0], json!(null));
assert_eq!(data2["matrix"][2][1], json!(5));
let mut data3 = json!({"arr": [1, 2, 3]});
set_nested_value(&mut data3, "arr.1", json!("replaced"));
assert_eq!(data3["arr"], json!([1, "replaced", 3]));
}
#[test]
fn test_hash_prefix_in_paths() {
let data = json!({
"fields": {
"20": "numeric field name",
"#": "hash field",
"##": "double hash field",
"normal": "normal field"
}
});
assert_eq!(
get_nested_value(&data, "fields.#20"),
Some(&json!("numeric field name"))
);
assert_eq!(
get_nested_value(&data, "fields.##"),
Some(&json!("hash field"))
);
assert_eq!(
get_nested_value(&data, "fields.###"),
Some(&json!("double hash field"))
);
assert_eq!(
get_nested_value(&data, "fields.normal"),
Some(&json!("normal field"))
);
assert_eq!(get_nested_value(&data, "fields.#999"), None);
}
#[test]
fn test_set_hash_prefix_in_paths() {
let mut data = json!({});
set_nested_value(&mut data, "fields.#20", json!("value for 20"));
assert_eq!(data["fields"]["20"], json!("value for 20"));
set_nested_value(&mut data, "fields.##", json!("hash value"));
assert_eq!(data["fields"]["#"], json!("hash value"));
set_nested_value(&mut data, "fields.###", json!("double hash value"));
assert_eq!(data["fields"]["##"], json!("double hash value"));
set_nested_value(&mut data, "fields.normal", json!("normal value"));
assert_eq!(data["fields"]["normal"], json!("normal value"));
assert_eq!(
data,
json!({
"fields": {
"20": "value for 20",
"#": "hash value",
"##": "double hash value",
"normal": "normal value"
}
})
);
}
#[test]
fn test_hash_prefix_with_arrays() {
let mut data = json!({
"items": [
{"0": "field named zero", "id": 1},
{"1": "field named one", "id": 2}
]
});
assert_eq!(
get_nested_value(&data, "items.0.#0"),
Some(&json!("field named zero"))
);
assert_eq!(
get_nested_value(&data, "items.1.#1"),
Some(&json!("field named one"))
);
set_nested_value(&mut data, "items.0.#2", json!("field named two"));
assert_eq!(data["items"][0]["2"], json!("field named two"));
assert_eq!(get_nested_value(&data, "items.0.id"), Some(&json!(1)));
assert_eq!(get_nested_value(&data, "items.1.id"), Some(&json!(2)));
}
#[test]
fn test_hash_prefix_field_with_array_value() {
let data = json!({
"data": {
"fields": {
"72": ["first", "second", "third"],
"100": ["alpha", "beta", "gamma"],
"normal": ["one", "two", "three"]
}
}
});
assert_eq!(
get_nested_value(&data, "data.fields.#72.0"),
Some(&json!("first"))
);
assert_eq!(
get_nested_value(&data, "data.fields.#72.1"),
Some(&json!("second"))
);
assert_eq!(
get_nested_value(&data, "data.fields.#72.2"),
Some(&json!("third"))
);
assert_eq!(
get_nested_value(&data, "data.fields.#100.0"),
Some(&json!("alpha"))
);
assert_eq!(
get_nested_value(&data, "data.fields.#100.1"),
Some(&json!("beta"))
);
assert_eq!(
get_nested_value(&data, "data.fields.normal.0"),
Some(&json!("one"))
);
let mut data_mut = data.clone();
set_nested_value(&mut data_mut, "data.fields.#72.0", json!("modified"));
assert_eq!(data_mut["data"]["fields"]["72"][0], json!("modified"));
set_nested_value(&mut data_mut, "data.fields.#999.0", json!("new value"));
assert_eq!(data_mut["data"]["fields"]["999"][0], json!("new value"));
let complex_data = json!({
"fields": {
"42": [
{"name": "item1", "value": 100},
{"name": "item2", "value": 200}
]
}
});
assert_eq!(
get_nested_value(&complex_data, "fields.#42.0.name"),
Some(&json!("item1"))
);
assert_eq!(
get_nested_value(&complex_data, "fields.#42.1.value"),
Some(&json!(200))
);
let multi_hash_data = json!({
"data": {
"#fields": {
"##": ["hash array"],
"10": ["numeric array"]
}
}
});
assert_eq!(
get_nested_value(&multi_hash_data, "data.##fields.###.0"),
Some(&json!("hash array"))
);
assert_eq!(
get_nested_value(&multi_hash_data, "data.##fields.#10.0"),
Some(&json!("numeric array"))
);
}
}