jsonp/
lib.rs

1//! # jsonp
2//!
3//! Fast, zero copy Json pointers.
4//!
5//! This library leverages [serde](https://serde.rs/) and [serde_json](https://docs.serde.rs/serde_json/index.html)
6//! to provide fast, easy to use, on demand deserialization of Json.
7//!
8//! Ever wanted to retrieve some deeply nested data without all the hassle of defining the required
9//! Rust structures, or allocating multiple times into a `serde_json::Value`? No problem:
10//!
11//! ```json
12//! {
13//!   "some": {
14//!     "deeply": [
15//!       {
16//!         "nested": {
17//!           "truth": "the cake is a lie"
18//!         }
19//!       }
20//!     ]
21//!   }
22//! }
23//! ```
24//!
25//! ```
26//! # use jsonp::Pointer;
27//! # type Result = std::result::Result<(), Box<dyn std::error::Error>>;
28//! # const NESTED: &str = r#"{"some": {"deeply": [{"nested": {"truth": "the cake is a lie"}}]}}"#;
29//! fn deeply_nested(json: &str) -> Result {
30//!     let p = Pointer::default();
31//!
32//!     let truth: &str = p.dotted(json, ".some.deeply.0.nested.truth")?;
33//!
34//!     assert_eq!(truth, "the cake is a lie");
35//!
36//!     Ok(())
37//! }
38//! # deeply_nested(NESTED).unwrap();
39//! ```
40//! Leveraging serde's [zero copy](https://serde.rs/lifetimes.html#understanding-deserializer-lifetimes) deserialization
41//! we _borrow_ the deeply nested `truth` right out of the backing Json data.
42//!
43//! ## Pointer
44//!
45//! The core structure of this library is the [`jsonp::Pointer`][pointer]. It provides several
46//! methods of dereferencing into Json:
47//!
48//! - [`Pointer::with_segments`][with_segments]
49//! - [`Pointer::with_pattern`][with_pattern]
50//! - [`Pointer::dotted`][dotted]
51//!
52//! While `Pointer::with_segments` provides the most control over exactly how each
53//! [`Segment`][segment] is generated, the other two -- `Pointer::with_pattern` and
54//! `Pointer::dotted` -- make some assumptions about how the pointer string is handled. These are:
55//!
56//! 1. Passing in an empty pointer (or pattern) string will default to deserializing the entire
57//!    backing Json.
58//! 2. If the pointer and pattern are equal, same as above.
59//! 3. A pointer starting with a pattern is equivalent to one that doesn't. For example,
60//!    `dotted(".foo")` and `dotted("foo")` both result in `foo` being dereferenced.
61//!
62//! ## Mode
63//!
64//! [`jsonp::Mode`][mode] controls how `jsonp` interprets pointer segments. It has two settings,
65//! Late -- the default -- and Early.
66//!
67//! ### Late
68//!
69//! Lazily attempts to coerce pointer segments to map keys or array indexes during deserialization.
70//! This provides maximum flexibility and allows one to deserialize _numeric_ map keys, i.e "42":
71//! "...", but also has the potential to improperly deserialize an array where a map was expected,
72//! or vice versa.
73//!
74//! ### Early
75//!
76//! Decides on initialization whether a given pointer segment is a numeric index or string key.
77//! Guarantees that backing Json object agrees with its expected layout, erroring out otherwise.
78//!
79//! ## Helpers
80//!
81//! This library also provides a few convenience wrapper structs around
82//! [`jsonp::Pointer`][pointer]. These provide pleasant interfaces if you're planning on using a
83//! `Pointer` to repeatedly dereference from a single backing Json structure, and reduce some of
84//! the generics and lifetime noise in function signatures.
85//!
86//! - [`jsonp::BackingStr`][b_str]
87//! - [`jsonp::BackingJson`][b_json]
88//! - [`jsonp::BackingBytes`][b_bytes]
89//!
90//! [pointer]: Pointer
91//! [mode]: Mode
92//! [segment]: Segment
93//! [b_str]: BackingStr
94//! [b_json]: BackingJson
95//! [b_bytes]: BackingBytes
96//! [with_segments]: Pointer::with_segments
97//! [with_pattern]: Pointer::with_pattern
98//! [dotted]: Pointer::dotted
99
100use {
101    json::value::RawValue as RawJson,
102    serde::{de::Deserializer as _, Deserialize},
103    serde_json as json,
104    std::iter::IntoIterator,
105    visitor::{ArrayVisitor, LazyVisitor, MapVisitor},
106};
107
108mod to_raw;
109mod visitor;
110
111pub use to_raw::ToRaw;
112
113/// The heart of this library, this structure contains all of the base
114/// functionality to dereference into borrowed Json structures.
115///
116/// While this struct does not implement [`Copy`][std::marker::Copy], it is extremely cheap to
117/// clone and can be done liberally.
118#[derive(Debug, Default, Clone)]
119pub struct Pointer {
120    mode: Mode,
121}
122
123impl Pointer {
124    /// Instantiate a new pointer with the given mode
125    pub fn new(mode: Mode) -> Self {
126        Self { mode }
127    }
128
129    /// Convenience function for using the common dot (`.`) delimited format for dereferencing
130    /// nested Json structures.
131    ///
132    /// # Example
133    ///
134    /// ```
135    /// use jsonp::Pointer;
136    ///
137    /// let json = r#"{"outer": {"array": [0, "one", true]}}"#;
138    /// let one: &str = Pointer::default().dotted(json, "outer.array.1").unwrap();
139    ///
140    /// assert!(one == "one");
141    /// ```
142    pub fn dotted<'de, 'j: 'de, J, T>(&self, backing: &'j J, pointer: &str) -> Result<T, J::Error>
143    where
144        J: ToRaw<'j> + ?Sized,
145        T: Deserialize<'de>,
146    {
147        self.with_pattern(backing, pointer, ".")
148    }
149
150    /// Dereference using the given pointer and pattern. The pointer is split into
151    /// segments using the pattern. Starting the pointer with or without the pattern
152    /// is equivalent, i.e: `with_pattern(..., ".foo", ".")` is equal to
153    /// `with_pattern(..., "foo", ".")`.
154    ///
155    /// Attempting to pass in either an empty pointer or pattern will cause this function
156    /// to short circuit any dereferencing and attempt deserialization from `backing`
157    /// directly.
158    ///
159    /// # Example
160    ///
161    /// ```
162    /// use jsonp::Pointer;
163    ///
164    /// let json = r#"{"outer": {"array": [0, "one", true]}}"#;
165    /// let is_true: bool = Pointer::default().with_pattern(json, "outer array 2", " ").unwrap();
166    ///
167    /// assert!(is_true);
168    /// ```
169    pub fn with_pattern<'de, 'j: 'de, J, T>(
170        &self,
171        backing: &'j J,
172        pointer: &str,
173        pattern: &str,
174    ) -> Result<T, J::Error>
175    where
176        J: ToRaw<'j> + ?Sized,
177        T: Deserialize<'de>,
178    {
179        let json = backing.try_into_raw()?;
180
181        // If the user attempts to pass any of the annoying edge cases around
182        // pattern splitting into us, we'll simply short circuit any dereferencing
183        // and attempt deserialization of the the entire backing object
184        if pointer.is_empty() || pattern.is_empty() || pointer == pattern {
185            return self.with_segments(json, None).map_err(Into::into);
186        }
187
188        // Allow users to not start a pointer with the given pattern
189        // if they choose. This is special cased to allow for situations
190        // where it would be annoying to require starting the pointer with a
191        // pattern instance, e.g: pat = ", " ptr = "foo, bar, baz".
192        if pointer.starts_with(pattern) {
193            let pointers = pointer.split(pattern).skip(1).map(|s| self.segment(s));
194
195            self.with_segments(json, pointers).map_err(Into::into)
196        } else {
197            let pointers = pointer.split(pattern).map(|s| self.segment(s));
198
199            self.with_segments(json, pointers).map_err(Into::into)
200        }
201    }
202
203    /// Dereference using the given iterable set of segments.
204    ///
205    /// # Example
206    ///
207    /// ```
208    /// use jsonp::{Pointer, Segment};
209    ///
210    /// let json = r#"{"outer": {"array": [0, 1, 2, 3]}}"#;
211    /// let segments = &["outer", "array"];
212    /// let array: Vec<i8> = Pointer::default()
213    ///     .with_segments(json, segments.into_iter().copied().map(Segment::lazy))
214    ///     .unwrap();
215    ///
216    /// assert_eq!(&array, &[0, 1, 2, 3]);
217    /// ```
218    pub fn with_segments<'de, 'j: 'de, 'p, J, I, T>(
219        &self,
220        backing: &'j J,
221        segments: I,
222    ) -> Result<T, J::Error>
223    where
224        J: ToRaw<'j> + ?Sized,
225        I: IntoIterator<Item = Segment<'p>>,
226        T: Deserialize<'de>,
227    {
228        let json = backing.try_into_raw()?;
229
230        inner(json, segments.into_iter()).map_err(Into::into)
231    }
232
233    fn segment<'p>(&self, s: &'p str) -> Segment<'p> {
234        match self.mode {
235            Mode::Late => Segment::lazy(s),
236            Mode::Early => Segment::early(s),
237        }
238    }
239}
240
241/// Convenience wrapper around the library core functions for string slices,
242/// removing some of the generic noise from function signatures.
243#[derive(Debug, Clone)]
244pub struct BackingStr<'a> {
245    p: Pointer,
246    borrow: &'a str,
247}
248
249impl<'a> BackingStr<'a> {
250    /// Instantiate a wrapper around the given string slice.
251    pub fn new(borrow: &'a str) -> Self {
252        Self::with(borrow, Default::default())
253    }
254
255    /// Instantiate a wrapper around the given string slice and pointer.
256    pub fn with(borrow: &'a str, p: Pointer) -> Self {
257        Self { p, borrow }
258    }
259
260    /// See the documentation of [`Pointer::dotted`][Pointer::dotted].
261    ///
262    /// # Example
263    ///
264    /// ```
265    /// use jsonp::BackingStr;
266    ///
267    /// let json = r#"{"outer": {"array": [0, "one", true]}}"#;
268    /// let one: &str = BackingStr::new(json).dotted("outer.array.1").unwrap();
269    ///
270    /// assert!(one == "one");
271    /// ```
272    pub fn dotted<'de, T>(&self, pointer: &str) -> Result<T, json::Error>
273    where
274        T: Deserialize<'de>,
275        'a: 'de,
276    {
277        self.p.dotted(self.borrow, pointer)
278    }
279
280    /// See the documentation of [`Pointer::with_pattern`][Pointer::with_pattern].
281    ///
282    /// # Example
283    ///
284    /// ```
285    /// use jsonp::BackingStr;
286    ///
287    /// let json = r#"{"outer": {"array": [0, "one", true]}}"#;
288    /// let is_true: bool = BackingStr::new(json).pattern("outer array 2", " ").unwrap();
289    ///
290    /// assert!(is_true);
291    /// ```
292    pub fn pattern<'de, T>(&self, pointer: &str, pattern: &str) -> Result<T, json::Error>
293    where
294        T: Deserialize<'de>,
295        'a: 'de,
296    {
297        self.p.with_pattern(self.borrow, pointer, pattern)
298    }
299
300    /// See the documentation for [`Pointer::with_segments`][Pointer::with_segments].
301    ///
302    /// # Example
303    ///
304    /// ```
305    /// use jsonp::{BackingStr, Segment};
306    ///
307    /// let json = r#"{"outer": {"array": [0, 1, 2, 3]}}"#;
308    /// let segments = &["outer", "array"];
309    /// let array: Vec<i8> = BackingStr::new(json)
310    ///     .pointer(segments.into_iter().copied().map(Segment::lazy))
311    ///     .unwrap();
312    ///
313    /// assert_eq!(&array, &[0, 1, 2, 3]);
314    /// ```
315    pub fn pointer<'de, 'p, I, T>(&self, pointers: I) -> Result<T, json::Error>
316    where
317        I: IntoIterator<Item = Segment<'p>>,
318        T: Deserialize<'de>,
319        'a: 'de,
320    {
321        self.p.with_segments(self.borrow, pointers)
322    }
323}
324
325impl<'a> From<&'a str> for BackingStr<'a> {
326    fn from(backing: &'a str) -> Self {
327        Self::new(backing)
328    }
329}
330
331/// Convenience wrapper around the library core functions for raw Json,
332/// removing some of the generic noise from function signatures.
333#[derive(Debug, Clone)]
334pub struct BackingJson<'a> {
335    p: Pointer,
336    borrow: &'a RawJson,
337}
338
339impl<'a> BackingJson<'a> {
340    /// Instantiate a wrapper around the given borrowed raw Json.
341    pub fn new(borrow: &'a RawJson) -> Self {
342        Self::with(borrow, Default::default())
343    }
344
345    /// Instantiate a wrapper around the given borrowed raw Json and pointer.
346    pub fn with(borrow: &'a RawJson, p: Pointer) -> Self {
347        Self { p, borrow }
348    }
349
350    /// See the documentation of [`Pointer::dotted`][Pointer::dotted].
351    ///
352    /// # Example
353    ///
354    /// ```
355    /// use {jsonp::{BackingJson, Segment}, serde_json::from_str};
356    ///
357    /// let json = from_str(r#"{"outer": {"array": [0, "one", true]}}"#).unwrap();
358    /// let one: &str = BackingJson::new(json).dotted("outer.array.1").unwrap();
359    ///
360    /// assert!(one == "one");
361    /// ```
362    pub fn dotted<'de, T>(&self, pointer: &str) -> Result<T, json::Error>
363    where
364        T: Deserialize<'de>,
365        'a: 'de,
366    {
367        self.p.dotted(self.borrow, pointer)
368    }
369
370    /// See the documentation of [`Pointer::with_pattern`][Pointer::with_pattern].
371    ///
372    /// # Example
373    ///
374    /// ```
375    /// use {jsonp::{BackingJson, Segment}, serde_json::from_str};
376    ///
377    /// let json = from_str(r#"{"outer": {"array": [0, "one", true]}}"#).unwrap();
378    /// let is_true: bool = BackingJson::new(json).pattern("outer array 2", " ").unwrap();
379    ///
380    /// assert!(is_true);
381    /// ```
382    pub fn pattern<'de, T>(&self, pointer: &str, pattern: &str) -> Result<T, json::Error>
383    where
384        T: Deserialize<'de>,
385        'a: 'de,
386    {
387        self.p.with_pattern(self.borrow, pointer, pattern)
388    }
389
390    /// See the documentation for [`Pointer::with_segments`][Pointer::with_segments].
391    ///
392    /// # Example
393    ///
394    /// ```
395    /// use {jsonp::{BackingJson, Segment}, serde_json::from_str};
396    ///
397    /// let json = from_str(r#"{"outer": {"array": [0, 1, 2, 3]}}"#).unwrap();
398    /// let segments = &["outer", "array"];
399    /// let array: Vec<i8> = BackingJson::new(json)
400    ///     .pointer(segments.into_iter().copied().map(Segment::lazy))
401    ///     .unwrap();
402    ///
403    /// assert_eq!(&array, &[0, 1, 2, 3]);
404    /// ```
405    pub fn pointer<'de, 'p, I, T>(&self, pointers: I) -> Result<T, json::Error>
406    where
407        I: IntoIterator<Item = Segment<'p>>,
408        T: Deserialize<'de>,
409        'a: 'de,
410    {
411        self.p.with_segments(self.borrow, pointers)
412    }
413}
414
415impl<'a> From<&'a RawJson> for BackingJson<'a> {
416    fn from(backing: &'a RawJson) -> Self {
417        Self::new(backing)
418    }
419}
420
421/// Convenience wrapper around the library core functions for byte slices,
422/// removing some of the generic noise from function signatures.
423#[derive(Debug, Clone)]
424pub struct BackingBytes<'a> {
425    p: Pointer,
426    borrow: &'a [u8],
427}
428
429impl<'a> BackingBytes<'a> {
430    /// Instantiate a wrapper around the given byte slice.
431    pub fn new(borrow: &'a [u8]) -> Self {
432        Self::with(borrow, Default::default())
433    }
434
435    /// Instantiate a wrapper around the given byte slice and pointer.
436    pub fn with(borrow: &'a [u8], pointer: Pointer) -> Self {
437        Self { p: pointer, borrow }
438    }
439
440    /// See the documentation of [`Pointer::dotted`][Pointer::dotted].
441    ///
442    /// # Example
443    ///
444    /// ```
445    /// use jsonp::BackingBytes;
446    ///
447    /// let json = r#"{"outer": {"array": [0, "one", true]}}"#.as_bytes();
448    /// let one: &str = BackingBytes::new(json).dotted("outer.array.1").unwrap();
449    ///
450    /// assert!(one == "one");
451    /// ```
452    pub fn dotted<'de, T>(&self, pointer: &str) -> Result<T, json::Error>
453    where
454        T: Deserialize<'de>,
455        'a: 'de,
456    {
457        self.p.dotted(self.borrow, pointer)
458    }
459
460    /// See the documentation of [`Pointer::with_pattern`][Pointer::with_pattern].
461    ///
462    /// # Example
463    ///
464    /// ```
465    /// use jsonp::BackingBytes;
466    ///
467    /// let json = r#"{"outer": {"array": [0, "one", true]}}"#.as_bytes();
468    /// let is_true: bool = BackingBytes::new(json).pattern("outer array 2", " ").unwrap();
469    ///
470    /// assert!(is_true);
471    /// ```
472    pub fn pattern<'de, T>(&self, pointer: &str, pattern: &str) -> Result<T, json::Error>
473    where
474        T: Deserialize<'de>,
475        'a: 'de,
476    {
477        self.p.with_pattern(self.borrow, pointer, pattern)
478    }
479
480    /// See the documentation for [`Pointer::with_segments`][Pointer::with_segments].
481    ///
482    /// # Example
483    ///
484    /// ```
485    /// use jsonp::{BackingBytes, Segment};
486    ///
487    /// let json = r#"{"outer": {"array": [0, 1, 2, 3]}}"#.as_bytes();
488    /// let segments = &["outer", "array"];
489    /// let array: Vec<i8> = BackingBytes::new(json)
490    ///     .pointer(segments.into_iter().copied().map(Segment::lazy))
491    ///     .unwrap();
492    ///
493    /// assert_eq!(&array, &[0, 1, 2, 3]);
494    /// ```
495    pub fn pointer<'de, 'p, I, T>(&self, pointers: I) -> Result<T, json::Error>
496    where
497        I: IntoIterator<Item = Segment<'p>>,
498        T: Deserialize<'de>,
499        'a: 'de,
500    {
501        self.p.with_segments(self.borrow, pointers)
502    }
503}
504
505impl<'a> From<&'a [u8]> for BackingBytes<'a> {
506    fn from(backing: &'a [u8]) -> Self {
507        Self::new(backing)
508    }
509}
510
511/// Set the mode for interpreting pointer segments.
512///
513/// The default, Late lazily types each segment waiting until
514/// deserialization to determine if the segment is a map key or
515/// array index. Early parses each segment as soon as it's handled.
516/// For more information, see `Segment`.
517#[derive(Debug, Clone, Copy, PartialEq, Eq)]
518pub enum Mode {
519    Late,
520    Early,
521}
522
523impl Default for Mode {
524    fn default() -> Self {
525        Self::Late
526    }
527}
528
529/// Represents a segment of a pointer
530#[derive(Debug, Clone, Copy)]
531pub struct Segment<'p> {
532    inner: PKind<'p>,
533}
534
535/// Typed representation of pointer segments
536#[derive(Debug, Clone, Copy)]
537enum PKind<'p> {
538    /// Delayed segment typing
539    Lazy(&'p str),
540    /// A map's key
541    Key(&'p str),
542    /// An index into an array
543    Index(u64),
544}
545
546impl<'p> Segment<'p> {
547    /// Lazily type the pointer, delaying the declaration
548    /// until called by the Json deserializer.
549    ///
550    /// Note this allows for processing of all valid Json
551    /// map keys; however, using this type _can_ also deserialize
552    /// Json arrays if the key is is parsable as a number.
553    ///
554    /// If you need strongly typed pointers see the `key` and
555    /// `index` methods.
556    pub fn lazy(s: &'p str) -> Self {
557        Self {
558            inner: PKind::Lazy(s),
559        }
560    }
561
562    /// Parse a pointer from a string slice, by attempting to convert it
563    /// to a number, and if successful setting it as an array index, otherwise
564    /// using it as a map key.
565    pub fn early(s: &'p str) -> Self {
566        use std::str::FromStr;
567
568        if s.is_empty() {
569            return Self::key("");
570        }
571
572        let inner = match u64::from_str(s).ok() {
573            Some(n) => PKind::Index(n),
574            None => PKind::Key(s),
575        };
576
577        Self { inner }
578    }
579
580    /// Generate a new map key segment
581    pub fn key(s: &'p str) -> Self {
582        Self {
583            inner: PKind::Key(s),
584        }
585    }
586
587    /// Generate a new array index segment
588    pub fn index(idx: u64) -> Self {
589        Self {
590            inner: PKind::Index(idx),
591        }
592    }
593}
594
595// Crate workhorse, this function abuses Rust's reference guarantees
596// to iteratively drill down a nested Json structure
597fn inner<'de, 'a: 'de, 'p, I, T>(j: &'a RawJson, p: I) -> Result<T, json::Error>
598where
599    I: Iterator<Item = Segment<'p>>,
600    T: Deserialize<'de>,
601{
602    use json::Deserializer;
603    let mut target = j;
604
605    for ptr in p {
606        let mut de = Deserializer::from_str(target.get());
607
608        match ptr.inner {
609            PKind::Lazy(l) => {
610                let value = de.deserialize_any(LazyVisitor::new(l))?;
611
612                target = value;
613            }
614            PKind::Key(k) => {
615                let value = de.deserialize_map(MapVisitor::new(k))?;
616
617                target = value;
618            }
619            PKind::Index(i) => {
620                let value = de.deserialize_seq(ArrayVisitor::new(i))?;
621
622                target = value;
623            }
624        }
625    }
626
627    serde_json::from_str(target.get()).map_err(Into::into)
628}
629
630#[cfg(test)]
631#[allow(unused_imports)]
632mod tests {
633    use super::*;
634    use pretty_assertions::{assert_eq, assert_ne};
635
636    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
637
638    const NESTED: &str =
639        r#"{"foo": {"bar": [0, "1", {"baz": "hello world!" }, "blitz", "fooey" ] } }"#;
640
641    #[test]
642    fn with_segments_str() -> Result {
643        let pointer = &[
644            Segment::key("foo"),
645            Segment::key("bar"),
646            Segment::index(2),
647            Segment::key("baz"),
648        ];
649
650        let output: &str =
651            Pointer::default().with_segments(NESTED, pointer.into_iter().copied())?;
652
653        assert_eq!(output, "hello world!");
654
655        Ok(())
656    }
657
658    #[test]
659    fn with_segments_json() -> Result {
660        let pointer = &[
661            Segment::key("foo"),
662            Segment::key("bar"),
663            Segment::index(2),
664            Segment::key("baz"),
665        ];
666
667        let json: &RawJson = serde_json::from_str(NESTED)?;
668
669        let output: &str = Pointer::default().with_segments(json, pointer.into_iter().copied())?;
670
671        assert_eq!(output, "hello world!");
672
673        Ok(())
674    }
675
676    #[test]
677    fn with_pattern_str() -> Result {
678        let output: &str = Pointer::default().with_pattern(NESTED, "foo, bar, 2, baz", ", ")?;
679
680        assert_eq!(output, "hello world!");
681
682        Ok(())
683    }
684
685    #[test]
686    fn with_pattern_json() -> Result {
687        let json: &RawJson = serde_json::from_str(NESTED)?;
688
689        let output: &str = Pointer::default().with_pattern(json, "foo, bar, 2, baz", ", ")?;
690
691        assert_eq!(output, "hello world!");
692
693        Ok(())
694    }
695
696    #[test]
697    fn with_pattern_empty() -> Result {
698        let object: &RawJson = json::from_str(NESTED)?;
699        let output: &RawJson = Pointer::default().with_pattern(NESTED, "", ", ")?;
700
701        assert_eq!(output.to_string(), object.to_string());
702
703        Ok(())
704    }
705
706    #[test]
707    fn dotted_str() -> Result {
708        let output: &str = Pointer::default().dotted(NESTED, "foo.bar.2.baz")?;
709
710        assert_eq!(output, "hello world!");
711
712        Ok(())
713    }
714
715    #[test]
716    fn dotted_json() -> Result {
717        let json: &RawJson = serde_json::from_str(NESTED)?;
718
719        let output: &str = Pointer::default().dotted(json, "foo.bar.2.baz")?;
720
721        assert_eq!(output, "hello world!");
722
723        Ok(())
724    }
725
726    #[test]
727    fn dotted_empty() -> Result {
728        let object: &RawJson = json::from_str(NESTED)?;
729        let output: &RawJson = Pointer::default().dotted(NESTED, "")?;
730
731        assert_eq!(output.to_string(), object.to_string());
732
733        Ok(())
734    }
735}