Home > database >  Deserialize JSON object (as text) inside JSON bject
Deserialize JSON object (as text) inside JSON bject

Time:12-24

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?

CodePudding user response:

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.

CodePudding user response:

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\"}"}
  • Related