1use serde_json::Value;
10
11pub fn get_nested_value<'b>(data: &'b Value, path: &str) -> Option<&'b Value> {
30 if path.is_empty() {
31 return Some(data);
32 }
33
34 let parts: Vec<&str> = path.split('.').collect();
35 let mut current = data;
36
37 for part in parts {
38 match current {
39 Value::Object(map) => {
40 let field_name = if let Some(stripped) = part.strip_prefix('#') {
42 stripped } else {
44 part
45 };
46 current = map.get(field_name)?;
47 }
48 Value::Array(arr) => {
49 let index = match part.parse::<usize>() {
51 Ok(idx) => idx,
52 Err(_) => return None, };
54
55 if index >= arr.len() {
57 return None; }
59
60 current = arr.get(index)?;
61 }
62 _ => return None, }
64 }
65
66 Some(current)
67}
68
69pub fn set_nested_value(data: &mut Value, path: &str, value: Value) {
95 let parts: Vec<&str> = path.split('.').collect();
96 let mut current = data;
97
98 for (i, part) in parts.iter().enumerate() {
99 if i == parts.len() - 1 {
100 match current {
102 Value::Object(map) => {
103 let field_name = if let Some(stripped) = part.strip_prefix('#') {
105 stripped } else {
107 part
108 };
109 map.insert(field_name.to_string(), value);
110 }
111 Value::Array(arr) => {
112 if let Ok(index) = part.parse::<usize>() {
114 while arr.len() <= index {
116 arr.push(Value::Null);
117 }
118 if index < arr.len() {
119 arr[index] = value;
120 }
121 }
122 }
123 _ => {}
124 }
125 return;
126 }
127
128 let next_is_array = parts
131 .get(i + 1)
132 .and_then(|p| p.parse::<usize>().ok())
133 .is_some();
134
135 match current {
136 Value::Object(map) => {
137 let field_name = if let Some(stripped) = part.strip_prefix('#') {
139 stripped } else {
141 if let Ok(_index) = part.parse::<usize>() {
143 return;
145 }
146 part
147 };
148
149 current = map.entry(field_name.to_string()).or_insert_with(|| {
151 if next_is_array {
152 Value::Array(Vec::new())
153 } else {
154 Value::Object(serde_json::Map::new())
155 }
156 });
157 }
158 Value::Array(arr) => {
159 if let Ok(index) = part.parse::<usize>() {
161 while arr.len() <= index {
163 arr.push(Value::Null);
164 }
165
166 if arr[index].is_null() {
168 arr[index] = if next_is_array {
169 Value::Array(Vec::new())
170 } else {
171 Value::Object(serde_json::Map::new())
172 };
173 }
174
175 current = &mut arr[index];
176 } else {
177 return;
179 }
180 }
181 _ => {
182 return;
184 }
185 }
186 }
187}
188
189pub fn get_nested_value_cloned(data: &Value, path: &str) -> Option<Value> {
200 get_nested_value(data, path).cloned()
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use serde_json::json;
207
208 #[test]
209 fn test_get_nested_value() {
210 let data = json!({
211 "user": {
212 "name": "John",
213 "age": 30,
214 "addresses": [
215 {"city": "New York", "zip": "10001"},
216 {"city": "San Francisco", "zip": "94102"}
217 ],
218 "preferences": {
219 "theme": "dark",
220 "notifications": true
221 }
222 },
223 "items": [1, 2, 3]
224 });
225
226 assert_eq!(get_nested_value(&data, "user.name"), Some(&json!("John")));
228 assert_eq!(get_nested_value(&data, "user.age"), Some(&json!(30)));
229
230 assert_eq!(
232 get_nested_value(&data, "user.preferences.theme"),
233 Some(&json!("dark"))
234 );
235 assert_eq!(
236 get_nested_value(&data, "user.preferences.notifications"),
237 Some(&json!(true))
238 );
239
240 assert_eq!(get_nested_value(&data, "items.0"), Some(&json!(1)));
242 assert_eq!(get_nested_value(&data, "items.2"), Some(&json!(3)));
243
244 assert_eq!(
246 get_nested_value(&data, "user.addresses.0.city"),
247 Some(&json!("New York"))
248 );
249 assert_eq!(
250 get_nested_value(&data, "user.addresses.1.zip"),
251 Some(&json!("94102"))
252 );
253
254 assert_eq!(get_nested_value(&data, "user.missing"), None);
256 assert_eq!(get_nested_value(&data, "items.10"), None);
257 assert_eq!(get_nested_value(&data, "user.addresses.2.city"), None);
258 assert_eq!(get_nested_value(&data, "nonexistent.path"), None);
259 }
260
261 #[test]
262 fn test_set_nested_value() {
263 let mut data = json!({});
264
265 set_nested_value(&mut data, "name", json!("Alice"));
267 assert_eq!(data, json!({"name": "Alice"}));
268
269 set_nested_value(&mut data, "user.email", json!("alice@example.com"));
271 assert_eq!(
272 data,
273 json!({
274 "name": "Alice",
275 "user": {"email": "alice@example.com"}
276 })
277 );
278
279 set_nested_value(&mut data, "name", json!("Bob"));
281 assert_eq!(
282 data,
283 json!({
284 "name": "Bob",
285 "user": {"email": "alice@example.com"}
286 })
287 );
288
289 set_nested_value(&mut data, "settings.theme.mode", json!("dark"));
291 assert_eq!(data["settings"]["theme"]["mode"], json!("dark"));
292
293 set_nested_value(&mut data, "user.age", json!(25));
295 assert_eq!(data["user"]["age"], json!(25));
296 assert_eq!(data["user"]["email"], json!("alice@example.com"));
297 }
298
299 #[test]
300 fn test_set_nested_value_with_arrays() {
301 let mut data = json!({
302 "items": [1, 2, 3]
303 });
304
305 set_nested_value(&mut data, "items.0", json!(10));
307 assert_eq!(data["items"], json!([10, 2, 3]));
308
309 set_nested_value(&mut data, "items.5", json!(50));
311 assert_eq!(data["items"], json!([10, 2, 3, null, null, 50]));
312
313 let mut data2 = json!({});
315 set_nested_value(&mut data2, "matrix.0.0", json!(1));
316 set_nested_value(&mut data2, "matrix.0.1", json!(2));
317 set_nested_value(&mut data2, "matrix.1.0", json!(3));
318 assert_eq!(
319 data2,
320 json!({
321 "matrix": [[1, 2], [3]]
322 })
323 );
324 }
325
326 #[test]
327 fn test_set_nested_value_array_expansion() {
328 let mut data = json!({});
329
330 set_nested_value(&mut data, "array.2", json!("value"));
332 assert_eq!(
333 data,
334 json!({
335 "array": [null, null, "value"]
336 })
337 );
338
339 let mut data2 = json!({});
341 set_nested_value(&mut data2, "deep.nested.0.field", json!("test"));
342 assert_eq!(
343 data2,
344 json!({
345 "deep": {
346 "nested": [{"field": "test"}]
347 }
348 })
349 );
350 }
351
352 #[test]
353 fn test_get_nested_value_cloned() {
354 let data = json!({
355 "user": {
356 "profile": {
357 "name": "Alice",
358 "settings": {"theme": "dark"}
359 }
360 }
361 });
362
363 let cloned = get_nested_value_cloned(&data, "user.profile.name");
365 assert_eq!(cloned, Some(json!("Alice")));
366
367 let cloned = get_nested_value_cloned(&data, "user.profile.settings");
369 assert_eq!(cloned, Some(json!({"theme": "dark"})));
370
371 let cloned = get_nested_value_cloned(&data, "user.missing");
373 assert_eq!(cloned, None);
374 }
375
376 #[test]
377 fn test_get_nested_value_bounds_checking() {
378 let data = json!({
379 "items": [1, 2, 3],
380 "nested": {
381 "array": [
382 {"id": 1},
383 {"id": 2}
384 ]
385 }
386 });
387
388 assert_eq!(get_nested_value(&data, "items.0"), Some(&json!(1)));
390 assert_eq!(get_nested_value(&data, "items.2"), Some(&json!(3)));
391
392 assert_eq!(get_nested_value(&data, "items.10"), None);
394 assert_eq!(get_nested_value(&data, "items.999999"), None);
395
396 assert_eq!(get_nested_value(&data, "items.abc"), None);
398 assert_eq!(get_nested_value(&data, "items.-1"), None);
399 assert_eq!(get_nested_value(&data, "items.2.5"), None);
400
401 assert_eq!(
403 get_nested_value(&data, "nested.array.0.id"),
404 Some(&json!(1))
405 );
406 assert_eq!(get_nested_value(&data, "nested.array.5.id"), None);
407
408 assert_eq!(get_nested_value(&data, ""), Some(&data));
410 }
411
412 #[test]
413 fn test_set_nested_value_bounds_safety() {
414 let mut data = json!({});
415
416 set_nested_value(&mut data, "large.10", json!("value"));
418 assert_eq!(data["large"].as_array().unwrap().len(), 11);
419 assert_eq!(data["large"][10], json!("value"));
420 for i in 0..10 {
421 assert_eq!(data["large"][i], json!(null));
422 }
423
424 let mut data2 = json!({"matrix": []});
426 set_nested_value(&mut data2, "matrix.2.1", json!(5));
427 assert_eq!(data2["matrix"][0], json!(null));
428 assert_eq!(data2["matrix"][1], json!(null));
429 assert_eq!(data2["matrix"][2][0], json!(null));
430 assert_eq!(data2["matrix"][2][1], json!(5));
431
432 let mut data3 = json!({"arr": [1, 2, 3]});
434 set_nested_value(&mut data3, "arr.1", json!("replaced"));
435 assert_eq!(data3["arr"], json!([1, "replaced", 3]));
436 }
437
438 #[test]
439 fn test_hash_prefix_in_paths() {
440 let data = json!({
442 "fields": {
443 "20": "numeric field name",
444 "#": "hash field",
445 "##": "double hash field",
446 "normal": "normal field"
447 }
448 });
449
450 assert_eq!(
452 get_nested_value(&data, "fields.#20"),
453 Some(&json!("numeric field name"))
454 );
455
456 assert_eq!(
458 get_nested_value(&data, "fields.##"),
459 Some(&json!("hash field"))
460 );
461
462 assert_eq!(
464 get_nested_value(&data, "fields.###"),
465 Some(&json!("double hash field"))
466 );
467
468 assert_eq!(
470 get_nested_value(&data, "fields.normal"),
471 Some(&json!("normal field"))
472 );
473
474 assert_eq!(get_nested_value(&data, "fields.#999"), None);
476 }
477
478 #[test]
479 fn test_set_hash_prefix_in_paths() {
480 let mut data = json!({});
481
482 set_nested_value(&mut data, "fields.#20", json!("value for 20"));
484 assert_eq!(data["fields"]["20"], json!("value for 20"));
485
486 set_nested_value(&mut data, "fields.##", json!("hash value"));
488 assert_eq!(data["fields"]["#"], json!("hash value"));
489
490 set_nested_value(&mut data, "fields.###", json!("double hash value"));
492 assert_eq!(data["fields"]["##"], json!("double hash value"));
493
494 set_nested_value(&mut data, "fields.normal", json!("normal value"));
496 assert_eq!(data["fields"]["normal"], json!("normal value"));
497
498 assert_eq!(
500 data,
501 json!({
502 "fields": {
503 "20": "value for 20",
504 "#": "hash value",
505 "##": "double hash value",
506 "normal": "normal value"
507 }
508 })
509 );
510 }
511
512 #[test]
513 fn test_hash_prefix_with_arrays() {
514 let mut data = json!({
515 "items": [
516 {"0": "field named zero", "id": 1},
517 {"1": "field named one", "id": 2}
518 ]
519 });
520
521 assert_eq!(
523 get_nested_value(&data, "items.0.#0"),
524 Some(&json!("field named zero"))
525 );
526
527 assert_eq!(
529 get_nested_value(&data, "items.1.#1"),
530 Some(&json!("field named one"))
531 );
532
533 set_nested_value(&mut data, "items.0.#2", json!("field named two"));
535 assert_eq!(data["items"][0]["2"], json!("field named two"));
536
537 assert_eq!(get_nested_value(&data, "items.0.id"), Some(&json!(1)));
539 assert_eq!(get_nested_value(&data, "items.1.id"), Some(&json!(2)));
540 }
541
542 #[test]
543 fn test_hash_prefix_field_with_array_value() {
544 let data = json!({
546 "data": {
547 "fields": {
548 "72": ["first", "second", "third"],
549 "100": ["alpha", "beta", "gamma"],
550 "normal": ["one", "two", "three"]
551 }
552 }
553 });
554
555 assert_eq!(
557 get_nested_value(&data, "data.fields.#72.0"),
558 Some(&json!("first"))
559 );
560
561 assert_eq!(
563 get_nested_value(&data, "data.fields.#72.1"),
564 Some(&json!("second"))
565 );
566
567 assert_eq!(
569 get_nested_value(&data, "data.fields.#72.2"),
570 Some(&json!("third"))
571 );
572
573 assert_eq!(
575 get_nested_value(&data, "data.fields.#100.0"),
576 Some(&json!("alpha"))
577 );
578 assert_eq!(
579 get_nested_value(&data, "data.fields.#100.1"),
580 Some(&json!("beta"))
581 );
582
583 assert_eq!(
585 get_nested_value(&data, "data.fields.normal.0"),
586 Some(&json!("one"))
587 );
588
589 let mut data_mut = data.clone();
591 set_nested_value(&mut data_mut, "data.fields.#72.0", json!("modified"));
592 assert_eq!(data_mut["data"]["fields"]["72"][0], json!("modified"));
593
594 set_nested_value(&mut data_mut, "data.fields.#999.0", json!("new value"));
596 assert_eq!(data_mut["data"]["fields"]["999"][0], json!("new value"));
597
598 let complex_data = json!({
600 "fields": {
601 "42": [
602 {"name": "item1", "value": 100},
603 {"name": "item2", "value": 200}
604 ]
605 }
606 });
607
608 assert_eq!(
609 get_nested_value(&complex_data, "fields.#42.0.name"),
610 Some(&json!("item1"))
611 );
612 assert_eq!(
613 get_nested_value(&complex_data, "fields.#42.1.value"),
614 Some(&json!(200))
615 );
616
617 let multi_hash_data = json!({
619 "data": {
620 "#fields": {
621 "##": ["hash array"],
622 "10": ["numeric array"]
623 }
624 }
625 });
626
627 assert_eq!(
629 get_nested_value(&multi_hash_data, "data.##fields.###.0"),
630 Some(&json!("hash array"))
631 );
632 assert_eq!(
633 get_nested_value(&multi_hash_data, "data.##fields.#10.0"),
634 Some(&json!("numeric array"))
635 );
636 }
637}