如何用Serde正确序列化含任意字符串的Rust枚举类型?
Rust Serde 枚举序列化:预定义变体与自定义字符串统一输出纯字符串
问题
需要实现一个枚举,既包含预定义的字符串变体,也支持任意字符串值,且序列化时所有变体都输出为纯字符串(而非嵌套对象)。当前实现中,自定义字符串变体被序列化为{"String":"hello"},添加#[serde(untagged)]后预定义变体又变成null,无法得到期望的结果。
现有代码
枚举定义:
#[derive(Serialize, Debug)] pub enum InvalidatedAreas { #[serde(rename = "all")] All, #[serde(rename = "stacks")] Stacks, #[serde(rename = "threads")] Threads, #[serde(rename = "variables")] Variables, String(String), }
测试代码:
#[derive(Serialize, Debug)] struct FooBar { foo: InvalidatedAreas, bar: InvalidatedAreas, } fn main() { let foob = FooBar { foo: InvalidatedAreas::Stacks, bar: InvalidatedAreas::String("hello".to_string()) }; let j = serde_json::to_string(&foob)?; println!("{}", j); }
当前输出与期望
- 当前输出:
{"foo":"stacks","bar":{"String":"hello"}} - 期望输出:
{"foo":"stacks","bar":"hello"} - 添加
#[serde(untagged)]后的错误输出:{"foo":null,"bar":"hello"}
解决方案
方法一:利用From trait + serde(into)属性
通过实现枚举到String的转换,让Serde在序列化时自动将枚举转为字符串:
use serde::Serialize; #[derive(Debug)] pub enum InvalidatedAreas { All, Stacks, Threads, Variables, String(String), } // 实现枚举到String的转换逻辑 impl From<InvalidatedAreas> for String { fn from(val: InvalidatedAreas) -> Self { match val { InvalidatedAreas::All => "all".to_string(), InvalidatedAreas::Stacks => "stacks".to_string(), InvalidatedAreas::Threads => "threads".to_string(), InvalidatedAreas::Variables => "variables".to_string(), InvalidatedAreas::String(s) => s, } } } // 在结构体成员上标注serde(into),指定序列化时转为String #[derive(Serialize, Debug)] struct FooBar { #[serde(into = "String")] foo: InvalidatedAreas, #[serde(into = "String")] bar: InvalidatedAreas, } fn main() -> Result<(), serde_json::Error> { let foob = FooBar { foo: InvalidatedAreas::Stacks, bar: InvalidatedAreas::String("hello".to_string()) }; let j = serde_json::to_string(&foob)?; println!("{}", j); Ok(()) }
方法二:手动实现Serialize trait
直接为枚举实现Serialize,精准控制每个变体的序列化行为:
use serde::{Serialize, Serializer}; #[derive(Debug)] pub enum InvalidatedAreas { All, Stacks, Threads, Variables, String(String), } impl Serialize for InvalidatedAreas { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { match self { InvalidatedAreas::All => serializer.serialize_str("all"), InvalidatedAreas::Stacks => serializer.serialize_str("stacks"), InvalidatedAreas::Threads => serializer.serialize_str("threads"), InvalidatedAreas::Variables => serializer.serialize_str("variables"), InvalidatedAreas::String(s) => serializer.serialize_str(s), } } } #[derive(Serialize, Debug)] struct FooBar { foo: InvalidatedAreas, bar: InvalidatedAreas, } fn main() -> Result<(), serde_json::Error> { let foob = FooBar { foo: InvalidatedAreas::Stacks, bar: InvalidatedAreas::String("hello".to_string()) }; let j = serde_json::to_string(&foob)?; println!("{}", j); Ok(()) }
为什么untagged无效?
#[serde(untagged)]要求枚举的所有变体都能被序列化为相同的顶层类型,但你的预定义变体是无数据的单元类型,Serde无法自动将其映射为字符串,因此会输出null。上述两种方法明确了每个变体对应的字符串值,因此能正确输出纯字符串格式。
内容的提问来源于stack exchange,提问作者Tamás Szelei
相关产品推荐
相关产品推荐

