You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 21:35:26