如何控制serde_json序列化结构体行为,实现Uuid与base62自动互转?
解决方案
1. 先添加必要依赖
在Cargo.toml中加入以下依赖,版本可根据项目实际情况调整:
[dependencies] uuid = { version = "1.0", features = ["serde", "v4", "sqlx"] } serde = { version = "1.0", features = ["derive"] } base62 = "2.0" sqlx = { version = "0.5", features = ["postgres", "runtime-tokio-native-tls", "uuid"] }
2. 实现Uuid和Base62互转的serde自定义逻辑
封装独立的序列化/反序列化模块,不会修改字段本身的Uuid类型,完全不影响sqlx的类型映射逻辑:
use serde::{Deserialize, Deserializer, Serializer}; use uuid::Uuid; pub mod uuid_base62 { use super::*; /// 将Uuid序列化为base62编码字符串 pub fn serialize<S>(uuid: &Uuid, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { let bytes = uuid.as_u128(); let base62_str = base62::encode(bytes); serializer.serialize_str(&base62_str) } /// 将base62编码字符串反序列化为Uuid pub fn deserialize<'de, D>(deserializer: D) -> Result<Uuid, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; let num = base62::decode(&s).map_err(serde::de::Error::custom)?; Ok(Uuid::from_u128(num)) } }
3. 给结构体字段添加serde属性
只需要在id字段上标注使用自定义转换逻辑即可,原有FromRow推导、query_as!调用方式完全不需要改动:
#[derive(Debug, Deserialize, sqlx::FromRow, Serialize)] pub struct Widget { #[serde(with = "uuid_base62")] pub id: Uuid, pub name: String, }
效果验证
- 数据库查询逻辑完全不变:
let widget = query_as!(Widget, r#"select * from widgets where id = $1"#, id).fetch_one(db_conn).await?;可以正常执行,sqlx能正确把数据库返回的uuid类型映射到结构体的Uuid字段 - 序列化响应给客户端时,
id字段会自动转为base62编码的字符串,比如"id": "7N3WXf8qZt2"格式 - 接收客户端请求时,传入的base62字符串会自动转成Uuid类型,不需要手动处理转换逻辑
如果多个结构体都需要这个转换能力,把uuid_base62模块抽到公共位置即可,所有需要转base62的Uuid字段都可以加#[serde(with = "uuid_base62")]复用,不需要重复写转换代码。
内容的提问来源于stack exchange,提问作者maxcountryman
相关产品推荐
相关产品推荐

