7

I would like to build a custom deserializer to deserialize an array of arrays of values into a Vec<Child> where I have already written a custom serde deserializer to parse an array of values into a Child.

One idea would be to add a customer deserializer for a Vec<Child> directly but I was wondering whether a more elegant solution would exist.


As an illustration I'm trying to make something like the below but with field array in Parent instead of single.

extern crate serde_json; // 1.0.32
extern crate serde; // 1.0.80
#[macro_use] extern crate serde_derive;

use serde::de::{Deserializer, SeqAccess, Visitor};
use std::fmt;

#[derive(Debug, Deserialize)]
struct Parent {
    #[serde(deserialize_with = "parse_child")]
    single: Child,
    //#[serde(deserialize_with = "parse_child")]
    //array: Vec<Child>,
}

#[derive(Default, Debug, Deserialize)]
struct Child {
    a: u64,
    b: f32,
    c: usize,
}

fn parse_child<'de, D>(deserializer: D) -> Result<Child, D::Error>
where
    D: Deserializer<'de>,
{
    struct ChildParser;
    impl<'de> Visitor<'de> for ChildParser
    {
        type Value = Child;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("[u64, f32, usize]")
        }

        fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
            println!("In custom deserializer");
            let mut child = Child { ..Default::default() };

            let tmp = seq.next_element::<u64>()?;
            if let Some(a) = tmp {
                child.a = a;
            };

            let tmp = seq.next_element::<f32>()?;
            if let Some(b) = tmp {
                child.b = b;
            };

            let tmp = seq.next_element::<usize>()?;
            if let Some(c) = tmp {
                child.c = c;
            };

            Ok(child)
        }
    }

    deserializer.deserialize_any(ChildParser{})
}

fn main() {
    let child_data = r#"[49, 11.75, 0]"#;
    let child : Child = serde_json::from_str(child_data).unwrap();
    println!("Child = {:?}", &child);

    let parent_data = r#"{"single": [49, 11.75, 0]}"#;
    let parent : Parent = serde_json::from_str(parent_data).expect("to be able to deserialize it");
    println!("Parent = {:?}", &parent);

}

Link to a playground

Sample input I want to deserialize: [[49, 11.75, 0], [42, 9, 1]]

3
  • 1
    @Stargateur there you go. Any idea on how I could solve this? Commented Nov 13, 2018 at 14:05
  • That much better but your question is still unclear. We don't know if you want to be able to deserialize r#"{"single": [49, 11.75, 0]}"# or r#"[[49, 11.75, 0], [42, 9, 1]]"# or both. Commented Nov 13, 2018 at 16:27
  • 2
    What trouble you with let parent: Vec<Child> = serde_json::from_str(parent_data).unwrap() ? Commented Nov 13, 2018 at 16:34

2 Answers 2

7

I would implement this as:

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[derive(Deserialize, Debug)]
#[serde(transparent)]
struct Parent {
    array: Vec<Child>,
}

#[derive(Deserialize, Debug)]
struct Child {
    a: u64,
    b: f32,
    c: usize,
}

fn main() {
    let j = r#" [[49, 11.75, 0], [42, 9, 1]] "#;
    println!("{:#?}", serde_json::from_str::<Parent>(j).unwrap());
}

Or more concisely:

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[derive(Deserialize, Debug)]
struct Child {
    a: u64,
    b: f32,
    c: usize,
}

fn main() {
    let j = r#" [[49, 11.75, 0], [42, 9, 1]] "#;
    let array: Vec<Child> = serde_json::from_str(j).unwrap();
    println!("{:#?}", array);
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot @dtolnay that's a super neat way to do it indeed. I didn't know (or understand) serde could deserialize struct from arrays directly
I'd suggest adding the transparent example to the linked duplicate.
0

I'm not sure if this is what you want but using the doc for deserialize a map:

extern crate serde; // 1.0.80
extern crate serde_json; // 1.0.32

use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
use std::fmt;

#[derive(Debug)]
struct Child {
    a: u64,
    b: f32,
    c: usize,
}

struct ChildVisitor;

impl<'de> Visitor<'de> for ChildVisitor {
    type Value = Child;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("[u64, f32, usize]")
    }

    fn visit_seq<A: SeqAccess<'de>>(self, mut access: A) -> Result<Self::Value, A::Error> {
        let a = access.next_element::<u64>()?.unwrap_or(Default::default());

        let b = access.next_element::<f32>()?.unwrap_or(Default::default());

        let c = access
            .next_element::<usize>()?
            .unwrap_or(Default::default());

        Ok(Child { a, b, c })
    }
}

impl<'de> Deserialize<'de> for Child {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_seq(ChildVisitor {})
    }
}

#[derive(Debug)]
struct Parent {
    childs: Vec<Child>,
}

struct ParentVisitor {}

impl<'de> Visitor<'de> for ParentVisitor {
    type Value = Parent;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("[[Child]]")
    }

    fn visit_seq<A: SeqAccess<'de>>(self, mut access: A) -> Result<Self::Value, A::Error> {
        let mut childs = Vec::with_capacity(access.size_hint().unwrap_or(0));

        while let Some(child) = access.next_element::<Child>()? {
            childs.push(child);
        }

        Ok(Parent { childs })
    }
}

impl<'de> Deserialize<'de> for Parent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_seq(ParentVisitor {})
    }
}

fn main() {
    let child_data = r#"[49, 11.75, 0]"#;
    let child: Child = serde_json::from_str(child_data).unwrap();
    println!("Child = {:#?}", child);

    let parent_data = r#"[[49, 11.75, 0], [42, 9, 1]]"#;
    let parent: Parent = serde_json::from_str(parent_data).unwrap();
    println!("Parent = {:#?}", parent);
}

1 Comment

Thanks @Stargateur indeed I just needed to implement manually Deserialize for Child instead of deriving it

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.