在反序列化枚举时,如何忽略大小写?

编程语言 2026-07-12

我正在为一个项目的配置文件编写解析器,我希望某个枚举值能够实现不区分大小写地解析。也就是说,无论最终用户如何书写这个值的大小写,serde每次都应把它解析为几个候选值中的一个。 (我本来可以要求值必须小写,但我想在这里尽量宽松一些,而且由于我只在启动时解析配置一次,我想我可以多花点CPU周期把它处理得尽量正确。)

例如:

use serde_derive::Deserialize;

#[derive(Deserialize)]
struct SmtpConfig {
    tls_mode: TlsMode,
}

#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
enum TlsMode {
    Smtps,
    StartTls,
    Unencrypted,
}

给出以下TOML...

tls_mode = 'startTLS'

…… 我希望 tls_mode 能解析为 TlsMode::StartTls。默认情况下不会,因为给定的数据与判别值的大小写并不完全匹配。

我期望 serde(rename_all) 在尝试解析之前就对传入的值进行重命名,但这似乎只是让serde期望数据已经是给定的大小写!这算合理,至少目前serde没有在解析时忽略大小写的方法(尽管该功能已被请求)。

这个情形类似于 How can I deserialize an enum when the case doesn't match?,但该问题的答案专门处理如何告诉serde期望哪种大小写,或者通过 serde(alias) 应用各种拼写,而不是直接强制将输入数据强制为某种大小写。

如何在不手动实现 Deserialize 的情况下,对这个枚举进行大小写不敏感的反序列化?

解决方案

(在大量浏览关于这一主题的 各种 文档问题 之后,我终于搞定了😌)

最终,我结合 strumserde_with 来实现这个效果:

use serde_with::DeserializeFromStr;
use strum::EnumString;

#[derive(DeserializeFromStr, EnumString)]
#[strum(ascii_case_insensitive)]
enum TlsMode {
    Smtps,
    StartTls,
    Unencrypted,
}

请注意这里移除了 serde(rename_all)

EnumString 派生宏为该枚举实现 FromStr,而 strum(ascii_case_insensitive) 使得生成的实现在解析时忽略(ASCII)大小写。然后 DeserializeFromStr 使用该枚举的 FromStr 实现来实现 Deserialize。就像魔法一样,现在枚举在反序列化时不再考虑大小写!

也许还有更好的方法来解决这个问题,但就不需要手动实现 FromStrDeserialize,也不必在枚举随时间变化时持续跟踪 match 的大小写情况而言,我认为这是一个还算可以的办法。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章