I can demonstrate my problem with this simple code:
#[derive(Debug, Deserialize)]
struct Person {
first_name: String,
last_name: String,
#[serde(deserialize_with = "full_name_deserializer")]
full_name: String,
}
fn full_name_deserializer<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let person: Person = Deserialize::deserialize(deserializer)?;
Ok(format!("{} - {}", person.first_name, person.last_name))
}
let data = r#"
{
"first_name": "John",
"last_name": "Doe"
}"#;
match serde_json::from_str::<Person>(data) {
Ok(p) => println!("Me: {:?}",p),
Err(e) => println!("Error: {:?}",e)
}
This throws error: Error("missing field
full_name", line: 5, column: 9)
The full_name
field will obviously be missing in the json because I want it to be initialized as the concatenation of 2 other fields. How can I do that?
EDIT: one idea I have is to remove deserialize_with
and to add skip_deserializing
to the full_name
field and then post process the struct to add the full_name after the deserialization is complete. I am not sure how to hook into the time when deserialization is complete? Is there a built in way? Or will I have to do it manually?
I did come across this:
https://github.com/serde-rs/serde/issues/642
which talks about "Add finalizer attribute hook to validate a deserialized structure". That might get me what I need. But I don't think it's been added to serde yet.
This does not work because it is missing the field. deserialize_with
gives you a deserializer for the field you want to deserialize, not the parent structure. The way it is currently written, it expects to find data which looks like this:
{
"first_name": "John",
"last_name": "Doe",
"full_name": {
"first_name": "John",
"last_name": "Doe"
}
}
That being said, it would still not work because the implementation of Person::deserialize
would call itself in an infinite loop until it eventually finds the inner field missing or overflows the stack.
You can fix this by implementing Deserialize
for the entire type, instead of just the derived field. Alternatively, you may find it easier to use serde(from = "...")
like so:
#[derive(Debug, Deserialize)]
#[serde(from = "PersonData")]
struct Person {
first_name: String,
last_name: String,
full_name: String,
}
#[derive(Debug, Deserialize)]
struct PersonData {
first_name: String,
last_name: String,
}
impl From<PersonData> for Person {
fn from(data: PersonData) -> Self {
Person {
full_name: format!("{} - {}", data.first_name, data.last_name),
first_name: data.first_name,
last_name: data.last_name,
}
}
}