如何在Rust结构体中定义动态类型字段适配JWT的amr声明?
解决JWT中amr声明的动态类型解码问题
方案一:用serde_with快速搞定(新手首选)
serde_with这个库专门解决serde的序列化/反序列化痛点,其中的OneOrMany工具能自动处理“单个值或数组”的场景,不用自己写复杂逻辑。
- 先在
Cargo.toml里添加依赖:
[dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" jsonwebtoken = "9.0" serde_with = "3.0"
- 定义Claims结构体时,给amr字段添加serde属性:
use serde::Deserialize; use serde_with::rust::one_or_many; #[derive(Debug, Deserialize)] struct Claims { // 其他字段(如sub、exp等)按需添加 #[serde(with = "one_or_many")] amr: Vec<String>, }
不管amr是单个字符串(比如"pwd")还是字符串数组(比如["pwd", "mfa"]),都会被解析成Vec<String>——单个字符串会自动转成仅含一个元素的数组,完美适配两种格式。
方案二:手动实现枚举反序列化(理解原理用)
如果不想引入额外依赖,可以自己定义枚举并实现Deserialize trait来处理两种情况:
- 定义覆盖两种场景的Amr枚举:
use serde::de::{self, Visitor, Deserializer}; use serde::Deserialize; use std::fmt; #[derive(Debug, Clone)] enum Amr { Single(String), Multiple(Vec<String>), } // 为Amr实现Deserialize trait impl<'de> Deserialize<'de> for Amr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { // 自定义Visitor处理两种类型 struct AmrVisitor; impl<'de> Visitor<'de> for AmrVisitor { type Value = Amr; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("单个字符串或字符串数组") } // 处理单个字符串的情况 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: de::Error, { Ok(Amr::Single(value.to_string())) } // 处理字符串数组的情况 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de>, { let mut vec = Vec::new(); while let Some(s) = seq.next_element()? { vec.push(s); } Ok(Amr::Multiple(vec)) } } deserializer.deserialize_any(AmrVisitor) } }
- 在Claims结构体中使用该枚举:
#[derive(Debug, Deserialize)] struct Claims { // 其他字段... amr: Amr, }
解码时两种格式的amr都能正确解析,之后可以通过match判断是单个值还是数组来做后续处理。
你之前枚举报错的原因
你之前用自定义枚举出现Err(Error("expected value", ...)),大概率是反序列化逻辑有问题——比如只处理了数组没处理字符串,或者没有用deserialize_any兼容两种类型,导致serde无法识别输入格式。上面的手动实现已经覆盖了两种场景,直接使用即可。
内容的提问来源于stack exchange,提问作者newbie
相关产品推荐
相关产品推荐

