skip to Main Content

I’m looking for a better solution for deserializing nested JSON object like this

{"name":"John Doe","age":43,"address":"{"street":"10 Downing Street","city":"London"}"}

Using this code I do the job

use serde_derive::{Deserialize, Serialize};use serde_json::{Result, Value};

#[derive(Serialize, Deserialize)]
struct Person{
    name: String,
    address: Value
}

#[derive(Debug, Serialize, Deserialize)]
struct Address{
    street: String,
    city: String
}
 impl From<Person> for Address{
    fn from(p: Person) -> Self {
        let str_val: String = serde_json::from_value(p.address).unwrap();
        let ad: Self =  serde_json::from_str(&str_val).unwrap();
        ad
    }
} 
fn main() -> Result<()> {
    let data = r#"{"name":"John Doe","age":43,"address":"{"street":"10 Downing Street","city":"London"}"}"#;
    let p: Person = serde_json::from_str(data).unwrap();
    println!("{:?}", Address::from(p));

    Ok(())

}

But It seems to me that may be a better way to do it. Any suggestion?

2

Answers


  1. But It seems to me that may be a better way to do it. Any suggestion?

    Well address: Value does not seem useful, because he Value in this case is a String: eprintln!("{:?}", p.address); will print

    String("{"street":"10 Downing Street","city":"London"}")
    

    So you could just have address: String, and then deserialise from that directly:

        serde_json::from_str(&p.address).unwrap()
    

    Alternatively you can use deserialize_with or create the deserializer for Person by hand, such that you can recursively invoke serde_json in order to deserialize Address while deserializing Person.

    Probably the biggest advantage is you should not have to allocate a string for Address, you can deserialize from the borrowed data.

    Login or Signup to reply.
  2. Adding to @Masklinn’s answer, here is a working version based on deserialize_with:

    use serde::{Deserialize, Deserializer};
    
    #[derive(Debug, Deserialize)]
    struct Address {
        street: String,
        city: String,
    }
    
    #[derive(Debug, Deserialize)]
    struct Person {
        name: String,
        #[serde(deserialize_with = "deserialize_nested_address")]
        address: Address,
    }
    
    fn deserialize_nested_address<'de, D>(data: D) -> Result<Address, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct AddressVisitor;
        impl<'de> serde::de::Visitor<'de> for AddressVisitor {
            type Value = Address;
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a string containing a json encoded address")
            }
            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Address, E> {
                serde_json::from_str(v).map_err(E::custom)
            }
        }
    
        data.deserialize_any(AddressVisitor)
    }
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let data = r#"{"name":"John Doe","age":43,"address":"{"street":"10 Downing Street","city":"London"}"}"#;
    
        let p: Person = serde_json::from_str(data)?;
        println!("{:#?}", p);
    
        Ok(())
    }
    
    Person {
        name: "John Doe",
        address: Address {
            street: "10 Downing Street",
            city: "London",
        },
    }
    

    You can of course do it both ways:

    use serde::{Deserialize, Deserializer, Serialize, Serializer};
    
    #[derive(Debug, Serialize, Deserialize)]
    struct Address {
        street: String,
        city: String,
    }
    
    #[derive(Debug, Serialize, Deserialize)]
    struct Person {
        name: String,
        #[serde(
            serialize_with = "serialize_nested_address",
            deserialize_with = "deserialize_nested_address"
        )]
        address: Address,
    }
    
    fn deserialize_nested_address<'de, D>(data: D) -> Result<Address, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct AddressVisitor;
        impl<'de> serde::de::Visitor<'de> for AddressVisitor {
            type Value = Address;
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a string containing a json encoded address")
            }
            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Address, E> {
                serde_json::from_str(v).map_err(E::custom)
            }
        }
    
        data.deserialize_any(AddressVisitor)
    }
    
    fn serialize_nested_address<S>(address: &Address, data: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        use serde::ser::Error;
    
        let s = serde_json::to_string(address).map_err(S::Error::custom)?;
        data.serialize_str(&s)
    }
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let data = r#"{"name":"John Doe","age":43,"address":"{"street":"10 Downing Street","city":"London"}"}"#;
    
        let p: Person = serde_json::from_str(data)?;
        println!("{:#?}", p);
    
        let p_ser = serde_json::to_string(&p)?;
        println!("Serialized: {}", p_ser);
    
        Ok(())
    }
    
    Person {
        name: "John Doe",
        address: Address {
            street: "10 Downing Street",
            city: "London",
        },
    }
    Serialized: {"name":"John Doe","address":"{"street":"10 Downing Street","city":"London"}"}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search