Search code examples
rustserde

Using serde attributes, can I convert before de/serialization and then use existing serde implementation?


I have an enum (imported from an external crate) that is called ExtEnum.

In my project I implemented a serde module called extenum_fromstr for it and I use it to de/serialize ExtEnum like so:

#[derive(Serialize, Deserialize)]
struct SomeStruct {
        #[serde(with = "extenum_fromstr")]
        ext_enum: ExtEnum,
        ...
}

This works fine.

Now, I have a different enum, let's call it MyEnum that can be converted to ExtEnum because it implements impl TryFrom<ExtEnum> for MyEnum and impl TryFrom<MyEnum> for ExtEnum.

I want to implement serde for MyEnum to behave just like the serde implementation for ExtEnum by taking advantage of the fact that I can convert MyEnum to ExtEnum.

Could I achieve this using only serde attributes? Something like:

#[serde(from = "ExtEnum", into = "ExtEnum", with = "extenum_fromstr")]
enum MyEnum {
        ...
}

This does not compile because with is not a valid container attribute. But is there a way to achieve this?


Solution

  • Ok, once again, we have:

    mod ext { pub enum Enum; }
    
    mod extenum_fromstr {
      pub fn deserialize(/* .. */) { .. }
    }
    
    impl TryFrom<ext::Enum> for My { .. }
    
    #[serde(..)] // how to reuse TryFrom & deserialize?
    enum My { .. }
    

    Suggested strategy:

    1. Implement Deserialize for an intermediate Helper type:
      #[serde(with = "extenum_fromstr")]
    2. Define TryFrom<Helper> for My
      (reusing TryFrom<ext::Enum>)
    3. Define My as one that can be deserialized through Helper
      #[serde(try_from = "Helper")]

    You can find the code with explanation on the playground.