'Deserializer never called when field is not present

I have a custom serializer and deserializer. where the a field is of type Option<i32>
and turns into stringified "i32" when serializing or skips the field if it is None.

The problem is when I try to deserialize back to struct which has no a field present. The print statements are never called either

Error:

thread 'main' panicked at 'called Result::unwrap() on an Err value: Error("missing field a", line: 1, column: 13)', src/main.rs:32:45

use std::error::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;

#[derive(Serialize, Deserialize, Debug)]
struct MyBox {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(serialize_with = "ser_a", deserialize_with = "de_a")]
    a: Option<i32>,
    b: String,
}

fn main() -> Result<(), Box<dyn Error>> {
  

    let e = MyBox {
        a: None,
        b: "oklol".to_string(),
    };

    let f: String = serde_json::to_string(&e).unwrap();
    let g: MyBox = serde_json::from_str(&f).unwrap(); // Error occurs here

    Ok(())
}

fn ser_a<S>(a: &Option<i32>, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let a = a.unwrap().to_string();
    s.serialize_str(&a)
}

fn de_a<'de, D>(d: D) -> Result<Option<i32>, D::Error>
where
    D: Deserializer<'de>,
{
    println!("{:#?}", ("hello!"));
    let opt: Option<String> = Option::deserialize(d)?;

    match opt {
        Some(s) => {
            let g = i32::from_str(&s);
            match g {
                Ok(x) => {
                    println!("hello1 {:#?}", (x));
                    Ok(Some(x))
                }
                Err(x) => {
                    println!("hello2 {:#?}", (x));
                    Ok(None)
                }
            }
        }
        None => {
            println!("hello3 {:#?}", ());
            Ok(None)
        }
    }
}



Solution 1:[1]

There are two things at play here.

  • When using custom deserializers, Options are no longer treated as optional when deserializing.
  • Custom deserializers (provided via deserialize_with) will only be called if the field is present in the structure to be deserialized.

If you want a default value for a field that is not present, you'll have to use either #[serde(default)] or #[serde(default = "path")] (docs here).


Note that user @jonasbb already provided this answer in the comments, but I think it would be worthwhile to have it as an answer.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 fresskoma