Skip to content

Add a new deserialize derive similar to DeserializeFromStr but that supports both the str and structural form #840

@leighmcculloch

Description

@leighmcculloch

The DeserializeFromStr derive has been super helpful in a number of applications for conveniently turning structs into simpler string representations of the same data.

In some situations where I've found myself introducing the use of DeserializeFromStr where it hadn't been used, I needed to support both the original structural form when deserializing as well as the new FromString form.

In those situations I wrote a deserialize impl like the below code.

I'm sharing it here because I think a derive that does something like this would be helpful and a good fit for serde_with. But I understand if it is also maybe a little too special case.

pub struct UInt128Parts {
    pub hi: u64,
    pub lo: u64,
}

impl<'de> serde::Deserialize<'de> for UInt128Parts {
    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::Deserialize;
        #[derive(Deserialize)]
        struct UInt128Parts {
            hi: u64,
            lo: u64,
        }
        #[derive(Deserialize)]
        #[serde(untagged)]
        enum UInt128PartsOrString<'a> {
            Str(&'a str),
            String(String),
            UInt128Parts(UInt128Parts),
        }
        match UInt128PartsOrString::deserialize(deserializer)? {
            UInt128PartsOrString::Str(s) => s.parse().map_err(serde::de::Error::custom),
            UInt128PartsOrString::String(s) => s.parse().map_err(serde::de::Error::custom),
            UInt128PartsOrString::UInt128Parts(UInt128Parts { hi, lo }) => {
                Ok(self::UInt128Parts { hi, lo })
            }
        }
    }
}

Ref: https://github.com/stellar/rs-stellar-xdr/pull/441/files#diff-7bc8ee8b0771a83456638e11ff0b378a2fdc8444fec02a25c1259b649e0859ce

Note, the above approach doesn't work well for all types uniformly. For example, certain types such as externally tagged enums that may render as a pure string in some cases and so the user has to be careful that the underlying deserializations don't overlap, or if they do, that it is okay.

Would a derive that does something similar to this be a good fit for serde_with?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions