Serde deserialization and Option

I found this previous discussion and it raises a question: I want a deserializer to support both (speaking toml here) count = 42 and count = "100K", and parse a toml into:

#[derive(Deserialize)]
struct Config {
  #[serde(with_deserializer = "de_count")]
  count: u64
}

I asked some related questions recently on here, and got the help I needed.

However, I wanted to change Config to contain count: Option<u64> instead. This didn't work, and adding default did not help. I assumed I needed to add a custom deserializer for Option<u64>, and indeed that does work. However, in the playground for the post referenced at the top of this post, there's no "duplicate" deserializer (one for none-Option and one for Option).

What is the magic sauce that makes Option Just Work(tm) in this code? The post explicitly says its a newtype. So is there an orphan rule coming in to play that's causing the limitation?

The playground you linked doesn’t use serde(with_deserializer = …) but a newtype pattern with its own Serialize. You could do the same, wrapping the u64 in a newtype, and that newtype can then work bare, or wrapped in Option. The “magic sauce” is that Deserialize<'_> for Option is generic

impl<'de, T> Deserialize<'de> for Option<T>
where
    T: Deserialize<'de>,

and calls back to the Deserialize<'_> for T inside, so Option<Newtype> will call the Newtype’s special impl as well.


If you prefer not to change the contained type, there’s another option: look into the “serde_with” crate is designed specifically to offer trait infrastructure for composable custom de-/serializers (for potentially external / pre-existing types). Like for Option, the same principle applies, they just provide a generic trait implementation to allow you to lift your custom deserializer around the option.

3 Likes

I'd use an untagged enum for intermediate representation.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.