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