jops/
json_path.rs

1use std::{
2    ops::{Deref, DerefMut},
3    str::FromStr,
4};
5
6use serde_json::Value;
7
8#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
9pub struct JsonPath(Vec<JsonPathElement>);
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub enum JsonPathElement {
13    Field(String), // key of an object
14    Index(JsonPathIndex),
15}
16
17impl ToString for JsonPathElement {
18    fn to_string(&self) -> String {
19        match self {
20            JsonPathElement::Field(v) => v.clone(),
21            JsonPathElement::Index(i) => i.to_string(),
22        }
23    }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
27pub enum JsonPathIndex {
28    NthLefth(usize), // N-th element from zero
29    NthRight(usize), // # represents the length of the array, #-1 is the last element
30}
31
32impl ToString for JsonPathIndex {
33    fn to_string(&self) -> String {
34        match self {
35            JsonPathIndex::NthLefth(i) => i.to_string(),
36            JsonPathIndex::NthRight(i) => format!("#-{i}"),
37        }
38    }
39}
40
41const ROOT: char = '$';
42const DOT: char = '.';
43const BEGIN_INDEX: char = '[';
44const CLOSE_INDEX: char = ']';
45const BEGIN_REVERSE_INDEX: char = '#';
46
47// Get element of arrray from right side without panic.
48fn get_right(array: &Vec<Value>, i: usize) -> Option<&Value> {
49    if array.len() < i {
50        None
51    } else {
52        array.get(array.len() - i)
53    }
54}
55
56// Get mutable element of array from right side without panic.
57fn get_right_mut(array: &mut Vec<Value>, i: usize) -> Option<&mut Value> {
58    if array.len() < i {
59        None
60    } else {
61        let i = array.len() - i;
62        array.get_mut(i)
63    }
64}
65
66impl Deref for JsonPath {
67    type Target = Vec<JsonPathElement>;
68
69    #[inline]
70    fn deref(&self) -> &Self::Target {
71        &self.0
72    }
73}
74
75impl DerefMut for JsonPath {
76    fn deref_mut(&mut self) -> &mut Self::Target {
77        &mut self.0
78    }
79}
80
81impl JsonPath {
82    pub fn find<'a>(&self, value: &'a Value) -> Option<&'a Value> {
83        let mut value = value;
84        for e in self.iter() {
85            let sub = match e {
86                JsonPathElement::Field(key) => value.get(key),
87                JsonPathElement::Index(JsonPathIndex::NthLefth(i)) => value.get(i),
88                JsonPathElement::Index(JsonPathIndex::NthRight(i)) => {
89                    value.as_array().and_then(|a| get_right(a, *i))
90                }
91            };
92            if let Some(sub) = sub {
93                value = sub;
94            } else {
95                return None;
96            }
97        }
98        Some(value)
99    }
100
101    pub fn find_mut<'a>(&self, value: &'a mut Value) -> Option<&'a mut Value> {
102        let mut value = value;
103        for e in self.iter() {
104            let sub = match e {
105                JsonPathElement::Field(key) => value.get_mut(key),
106                JsonPathElement::Index(JsonPathIndex::NthLefth(i)) => value.get_mut(i),
107                JsonPathElement::Index(JsonPathIndex::NthRight(i)) => {
108                    value.as_array_mut().and_then(|a| get_right_mut(a, *i))
109                }
110            };
111            if let Some(sub) = sub {
112                value = sub;
113            } else {
114                return None;
115            }
116        }
117        Some(value)
118    }
119
120    pub fn insert<'a>(&self, value: &'a mut Value, v: Value) -> Option<&'a Value> {
121        match self.find_last_mut(value) {
122            Some((Value::Array(target), JsonPathElement::Index(JsonPathIndex::NthLefth(i)))) => {
123                let i = *i;
124                if i <= target.len() {
125                    target.insert(i, v);
126                    Some(value)
127                } else {
128                    None
129                }
130            }
131            Some((Value::Array(target), JsonPathElement::Index(JsonPathIndex::NthRight(i)))) => {
132                if target.len() < *i {
133                    return None;
134                }
135                let i = target.len() - i;
136                if i <= target.len() {
137                    target.insert(i, v);
138                    Some(value)
139                } else {
140                    None
141                }
142            }
143            Some((Value::Object(target), JsonPathElement::Field(key))) => {
144                if target.contains_key(key) {
145                    None
146                } else {
147                    target.insert(key.clone(), v);
148                    Some(value)
149                }
150            }
151            _ => None,
152        }
153    }
154
155    pub fn replace<'a>(&self, value: &'a mut Value, v: Value) -> Option<&'a Value> {
156        if let Some(target) = self.find_mut(value) {
157            *target = v;
158            Some(value)
159        } else {
160            None
161        }
162    }
163
164    pub fn set<'a>(&self, value: &'a mut Value, v: Value) -> Option<&'a Value> {
165        match self.find_last_mut(value) {
166            Some((Value::Array(target), JsonPathElement::Index(JsonPathIndex::NthLefth(i)))) => {
167                if let Some(target) = target.get_mut(*i) {
168                    *target = v;
169                    Some(value)
170                } else {
171                    None
172                }
173            }
174            Some((Value::Array(target), JsonPathElement::Index(JsonPathIndex::NthRight(i)))) => {
175                if let Some(target) = get_right_mut(target, *i) {
176                    *target = v;
177                    Some(value)
178                } else {
179                    None
180                }
181            }
182            Some((Value::Object(target), JsonPathElement::Field(key))) => {
183                target.insert(key.clone(), v);
184                Some(value)
185            }
186            _ => None,
187        }
188    }
189
190    pub fn remove<'a>(&self, value: &'a mut Value) -> Option<&'a Value> {
191        match self.find_last_mut(value) {
192            Some((Value::Array(target), JsonPathElement::Index(JsonPathIndex::NthLefth(i)))) => {
193                if target.len() < *i {
194                    None
195                } else {
196                    target.remove(*i);
197                    Some(value)
198                }
199            }
200            Some((Value::Array(target), JsonPathElement::Index(JsonPathIndex::NthRight(i)))) => {
201                if target.len() < *i {
202                    None
203                } else {
204                    let i = target.len() - i;
205                    target.remove(i);
206                    Some(value)
207                }
208            }
209            Some((Value::Object(target), JsonPathElement::Field(key))) => {
210                if target.remove(key).is_some() {
211                    Some(value)
212                } else {
213                    None
214                }
215            }
216            _ => None,
217        }
218    }
219
220    fn find_last_mut<'a>(&self, value: &'a mut Value) -> Option<(&'a mut Value, &JsonPathElement)> {
221        self.split_last().and_then(|(last, rest)| {
222            JsonPath(rest.to_vec())
223                .find_mut(value)
224                .map(|target| (target, last))
225        })
226    }
227}
228
229impl FromStr for JsonPath {
230    type Err = &'static str;
231    fn from_str(value: &str) -> Result<Self, Self::Err> {
232        let mut iter = value.chars().peekable();
233
234        match iter.peek() {
235            Some(&ROOT) => _ = iter.next(),
236            Some(v) if v.is_numeric() => {
237                let mut field = String::new();
238                while let Some(c) = iter.next_if(|c| c.is_numeric()) {
239                    field.push(c);
240                }
241                let index =
242                    JsonPathElement::Index(JsonPathIndex::NthLefth(field.parse().unwrap_or(0)));
243                return Ok(JsonPath(vec![index]));
244            }
245            _ => return Err("expected $ or numeric"),
246        };
247
248        let mut path: Vec<JsonPathElement> = Vec::new();
249        loop {
250            match iter.next() {
251                Some(DOT) => {
252                    let mut field: String = String::new();
253                    while let Some(c) = iter.next_if(|c| c.is_alphabetic()) {
254                        field.push(c);
255                    }
256                    path.push(JsonPathElement::Field(field));
257                }
258                Some(BEGIN_INDEX) => {
259                    let index = if iter.next_if_eq(&BEGIN_REVERSE_INDEX).is_some() {
260                        iter.next_if_eq(&'-');
261                        JsonPathIndex::NthRight(0)
262                    } else {
263                        JsonPathIndex::NthLefth(0)
264                    };
265
266                    let mut field: String = String::new();
267                    while let Some(c) = iter.next_if(|c| c.is_numeric()) {
268                        field.push(c);
269                    }
270                    if iter.next_if_eq(&CLOSE_INDEX).is_none() {
271                        return Err("expected ]");
272                    }
273                    let index = match index {
274                        JsonPathIndex::NthLefth(_) => {
275                            JsonPathIndex::NthLefth(field.parse().unwrap_or(0))
276                        }
277                        JsonPathIndex::NthRight(_) => {
278                            JsonPathIndex::NthRight(field.parse().unwrap_or(0))
279                        }
280                    };
281                    path.push(JsonPathElement::Index(index));
282                }
283                None => return Ok(JsonPath(path)),
284                _ => return Err("expected . or ["),
285            }
286        }
287    }
288}
289
290impl TryFrom<&str> for JsonPath {
291    type Error = &'static str;
292
293    #[inline]
294    fn try_from(value: &str) -> Result<Self, Self::Error> {
295        JsonPath::from_str(value)
296    }
297}
298
299pub trait JsonPathQuery<'a> {
300    fn path(&'a self, query: &str) -> Result<&'a Value, &'static str>;
301    fn path_mut(&'a mut self, query: &str) -> Result<&'a mut Value, &'static str>;
302}
303
304impl<'a> JsonPathQuery<'a> for Value {
305    #[inline]
306    fn path(&'a self, query: &str) -> Result<&'a Value, &'static str> {
307        let path = JsonPath::try_from(query)?;
308        path.find(self).ok_or("unable to find path to value")
309    }
310
311    #[inline]
312    fn path_mut(&'a mut self, query: &str) -> Result<&'a mut Value, &'static str> {
313        let path = JsonPath::try_from(query)?;
314        path.find_mut(self).ok_or("unable to find path to value")
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use serde_json::json;
321
322    use super::*;
323
324    #[test]
325    fn try_from() {
326        let tests = vec![
327            ("$", Ok(JsonPath(vec![]))),
328            (
329                "3",
330                Ok(JsonPath(vec![JsonPathElement::Index(
331                    JsonPathIndex::NthLefth(3),
332                )])),
333            ),
334            (
335                "$.a",
336                Ok(JsonPath(vec![JsonPathElement::Field("a".to_string())])),
337            ),
338            (
339                "$.a.b",
340                Ok(JsonPath(vec![
341                    JsonPathElement::Field("a".to_string()),
342                    JsonPathElement::Field("b".to_string()),
343                ])),
344            ),
345            (
346                "$.abc.bc.cbc",
347                Ok(JsonPath(vec![
348                    JsonPathElement::Field("abc".to_string()),
349                    JsonPathElement::Field("bc".to_string()),
350                    JsonPathElement::Field("cbc".to_string()),
351                ])),
352            ),
353            (
354                "$[4]",
355                Ok(JsonPath(vec![JsonPathElement::Index(
356                    JsonPathIndex::NthLefth(4),
357                )])),
358            ),
359            (
360                "$[4][3]",
361                Ok(JsonPath(vec![
362                    JsonPathElement::Index(JsonPathIndex::NthLefth(4)),
363                    JsonPathElement::Index(JsonPathIndex::NthLefth(3)),
364                ])),
365            ),
366            (
367                "$.a[4].b[3]",
368                Ok(JsonPath(vec![
369                    JsonPathElement::Field("a".to_string()),
370                    JsonPathElement::Index(JsonPathIndex::NthLefth(4)),
371                    JsonPathElement::Field("b".to_string()),
372                    JsonPathElement::Index(JsonPathIndex::NthLefth(3)),
373                ])),
374            ),
375            (
376                "$.a[#-4].b[3]",
377                Ok(JsonPath(vec![
378                    JsonPathElement::Field("a".to_string()),
379                    JsonPathElement::Index(JsonPathIndex::NthRight(4)),
380                    JsonPathElement::Field("b".to_string()),
381                    JsonPathElement::Index(JsonPathIndex::NthLefth(3)),
382                ])),
383            ),
384            (
385                "$.a[#]",
386                Ok(JsonPath(vec![
387                    JsonPathElement::Field("a".to_string()),
388                    JsonPathElement::Index(JsonPathIndex::NthRight(0)),
389                ])),
390            ),
391            // Invalid
392            (".a", Err("expected $ or numeric")),
393            ("a", Err("expected $ or numeric")),
394            ("[0]", Err("expected $ or numeric")),
395            ("$0]", Err("expected . or [")),
396        ];
397        for (path, expected) in tests {
398            assert_eq!(
399                path.try_into(),
400                expected,
401                "expected {} to be {:?}",
402                path,
403                expected
404            );
405        }
406    }
407
408    #[test]
409    fn path() {
410        let tests: Vec<(&str, serde_json::Value, Result<serde_json::Value, _>)> = vec![
411            ("$", json!({}), Ok(json!({}))),
412            ("$.a", json!({"a":"example"}), Ok(json!("example"))),
413            ("$[0]", json!([0, 1, 2, 3]), Ok(json!(0))),
414            ("$[#-1]", json!([0, 1, 2, 3]), Ok(json!(3))),
415            (
416                "$.a[#-1].b[0].test",
417                json!({"a": [
418                    {
419                        "b": "invalid"
420                    },
421                    {
422                        "b": [{ "test": "example"}, { "test": "invalid" }]
423                    }
424                ],
425                "b": "invalid"
426                }),
427                Ok(json!("example")),
428            ),
429            ("1", json!([1, 2, 4]), Ok(json!(2))),
430            ("$[2]", json!([1]), Err("unable to find path to value")),
431            (
432                "$.a[#-2]",
433                json!({ "b": [1] }),
434                Err("unable to find path to value"),
435            ),
436        ];
437
438        for (path, value, expected) in tests {
439            assert_eq!(
440                value.path(path).cloned(),
441                expected,
442                "expected {} from {} to be {:?}",
443                value,
444                path,
445                expected
446            );
447            let mut value = value;
448            assert_eq!(
449                value.path_mut(path).cloned(),
450                expected,
451                "expected {} from {} to be {:?}",
452                value,
453                path,
454                expected
455            );
456        }
457    }
458
459    #[test]
460    fn insert() {
461        let tests: Vec<(
462            JsonPath,
463            serde_json::Value,
464            serde_json::Value,
465            Option<serde_json::Value>,
466        )> = vec![
467            (
468                "$.a".try_into().unwrap(),
469                json!({}),
470                json!("test"),
471                Some(json!({ "a": "test"})),
472            ),
473            (
474                "$.a.b[1]".try_into().unwrap(),
475                json!({"a": { "b": [1,2,4] }}),
476                json!("test"),
477                Some(json!({ "a": { "b": [1, "test", 2, 4]}})),
478            ),
479            (
480                "$.a.b[#]".try_into().unwrap(),
481                json!({"a": { "b": [1,2,4] }}),
482                json!("test"),
483                Some(json!({ "a": { "b": [1, 2, 4, "test"]}})),
484            ),
485            (
486                "$.a.b[#-3]".try_into().unwrap(),
487                json!({"a": { "b": [1,2,4] }}),
488                json!("test"),
489                Some(json!({ "a": { "b": ["test", 1, 2, 4 ]}})),
490            ),
491            (
492                "$.a".try_into().unwrap(),
493                json!({"a": 10.0}),
494                json!("test"),
495                None,
496            ),
497            (
498                "$.a[1]".try_into().unwrap(),
499                json!({"a": []}),
500                json!("test"),
501                None,
502            ),
503            (
504                "$.a[#-3]".try_into().unwrap(),
505                json!({"a": []}),
506                json!("test"),
507                None,
508            ),
509        ];
510
511        for (path, mut value, extra, expected) in tests {
512            let value = path.insert(&mut value, extra);
513            assert_eq!(
514                value,
515                expected.as_ref(),
516                "expected {:?} to be {:?}",
517                value,
518                expected
519            );
520        }
521    }
522
523    #[test]
524    fn replace() {
525        let tests: Vec<(
526            JsonPath,
527            serde_json::Value,
528            serde_json::Value,
529            Option<serde_json::Value>,
530        )> = vec![
531            ("$.a".try_into().unwrap(), json!({}), json!("test"), None),
532            (
533                "$.a.b[1]".try_into().unwrap(),
534                json!({"a": { "b": [1,2,4] }}),
535                json!("test"),
536                Some(json!({ "a": { "b": [1, "test", 4]}})),
537            ),
538            (
539                "$.a.b[#-2]".try_into().unwrap(),
540                json!({"a": { "b": [1,2,4] }}),
541                json!("test"),
542                Some(json!({ "a": { "b": [1, "test", 4 ]}})),
543            ),
544            (
545                "$.a".try_into().unwrap(),
546                json!({"a": 10.0}),
547                json!("test"),
548                Some(json!({"a": "test"})),
549            ),
550            (
551                "$.a[1]".try_into().unwrap(),
552                json!({"a": []}),
553                json!("test"),
554                None,
555            ),
556            (
557                "$.a[#-3]".try_into().unwrap(),
558                json!({"a": []}),
559                json!("test"),
560                None,
561            ),
562        ];
563
564        for (path, mut value, extra, expected) in tests {
565            let value = path.replace(&mut value, extra);
566            assert_eq!(
567                value,
568                expected.as_ref(),
569                "expected {:?} to be {:?}",
570                value,
571                expected
572            );
573        }
574    }
575
576    #[test]
577    fn set() {
578        let tests: Vec<(
579            JsonPath,
580            serde_json::Value,
581            serde_json::Value,
582            Option<serde_json::Value>,
583        )> = vec![
584            (
585                "$.a".try_into().unwrap(),
586                json!({}),
587                json!("test"),
588                Some(json!({ "a": "test" })),
589            ),
590            (
591                "$.a.b[1]".try_into().unwrap(),
592                json!({"a": { "b": [1,2,4] }}),
593                json!("test"),
594                Some(json!({ "a": { "b": [1, "test", 4]}})),
595            ),
596            (
597                "$.a.b[#-2]".try_into().unwrap(),
598                json!({"a": { "b": [1,2,4] }}),
599                json!("test"),
600                Some(json!({ "a": { "b": [1, "test", 4 ]}})),
601            ),
602            (
603                "$.a".try_into().unwrap(),
604                json!({"a": 10.0}),
605                json!("test"),
606                Some(json!({"a": "test"})),
607            ),
608            (
609                "$.a[1]".try_into().unwrap(),
610                json!({"a": []}),
611                json!("test"),
612                None,
613            ),
614            (
615                "$.a[#-3]".try_into().unwrap(),
616                json!({"a": []}),
617                json!("test"),
618                None,
619            ),
620        ];
621
622        for (path, mut value, extra, expected) in tests {
623            let value = path.set(&mut value, extra);
624            assert_eq!(
625                value,
626                expected.as_ref(),
627                "expected {:?} to be {:?}",
628                value,
629                expected
630            );
631        }
632    }
633
634    #[test]
635    fn remove() {
636        let tests: Vec<(JsonPath, serde_json::Value, Option<serde_json::Value>)> = vec![
637            ("$.a".try_into().unwrap(), json!({}), None),
638            (
639                "$.a.b[1]".try_into().unwrap(),
640                json!({"a": { "b": [1,2,8] }}),
641                Some(json!({ "a": { "b": [1, 8]}})),
642            ),
643            (
644                "$.a.b[#-2]".try_into().unwrap(),
645                json!({"a": { "b": [1,2,4] }}),
646                Some(json!({ "a": { "b": [1, 4 ]}})),
647            ),
648            (
649                "$.a".try_into().unwrap(),
650                json!({"a": 10.0}),
651                Some(json!({})),
652            ),
653            ("$.a[1]".try_into().unwrap(), json!({"a": []}), None),
654            ("$.a[#-3]".try_into().unwrap(), json!({"a": []}), None),
655        ];
656
657        for (path, mut value, expected) in tests {
658            let value = path.remove(&mut value);
659            assert_eq!(
660                value,
661                expected.as_ref(),
662                "expected {:?} to be {:?}",
663                value,
664                expected
665            );
666        }
667    }
668}