能否仅通过serde属性,借助ExtEnum转换实现MyEnum的序列化反序列化?
问题(中文翻译)
我有一个来自外部 crate 的枚举 ExtEnum,已经为它实现了 serde 模块 extenum_fromstr,用于在结构体 SomeStruct 中完成序列化与反序列化。现在我有自定义枚举 MyEnum,它与 ExtEnum 实现了双向 TryFrom 转换,希望让 MyEnum 的 serde 行为与 ExtEnum 完全一致。尝试使用 #[serde(from = "ExtEnum", into = "ExtEnum", with = "extenum_fromstr")] 标记 MyEnum,但因 with 不是合法容器属性编译失败,请问能否仅通过 serde 属性实现该需求?
解决方案
仅通过原生 serde 容器属性无法直接实现目标(with 是字段属性,不能用于枚举/结构体的容器级别),但可以通过两种间接方式结合 serde 属性达成需求,无需手动编写完整的 Serialize/Deserialize 实现:
方案一:借助 serde_with 库简化转换逻辑
利用 serde_with 提供的 TryFromInto 适配器,自动处理 TryFrom/TryInto 的错误,同时复用 ExtEnum 已有的 extenum_fromstr 序列化逻辑:
- 添加依赖到
Cargo.toml:
serde = { version = "1.0", features = ["derive"] } serde_with = "3.0"
- 给
MyEnum添加属性标记:
use serde::{Deserialize, Serialize}; use serde_with::serde_as; // 导入外部 crate 的枚举和 serde 模块 extern crate external_crate; use external_crate::{ExtEnum, extenum_fromstr}; #[serde_as] #[derive(Serialize, Deserialize)] #[serde(from = "ExtEnum", into = "ExtEnum")] #[serde_as(as = "TryFromInto<ExtEnum>")] enum MyEnum { VarA, VarB, // 自定义枚举变体 }
方案二:手动封装适配模块(无额外依赖)
自己编写一个简单的 serde 适配模块,将 MyEnum 的序列化/反序列化逻辑委托给 ExtEnum 的 extenum_fromstr,同时处理 TryFrom 的错误:
use serde::{Deserializer, Serializer}; use external_crate::{ExtEnum, extenum_fromstr}; // 自定义 serde 适配模块 mod myenum_serde { use super::*; pub fn serialize<S>(value: &MyEnum, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { let ext_enum: ExtEnum = value.try_into().map_err(serde::ser::Error::custom)?; extenum_fromstr::serialize(&ext_enum, serializer) } pub fn deserialize<'de, D>(deserializer: D) -> Result<MyEnum, D::Error> where D: Deserializer<'de>, { let ext_enum = extenum_fromstr::deserialize(deserializer)?; MyEnum::try_from(ext_enum).map_err(serde::de::Error::custom) } } // 给 MyEnum 关联适配模块 #[derive(Serialize, Deserialize)] #[serde(with = "myenum_serde")] enum MyEnum { VarA, VarB, // 自定义枚举变体 }
原写法失败原因
#[serde(with = "...")] 是字段专属属性,仅能用于结构体的字段定义上,不能直接作用于枚举/结构体的容器级别;而 from/into 是容器属性,两者无法直接在容器上组合使用。
内容的提问来源于stack exchange,提问作者b2238488

