能否像Serde一样将含HashMap的Enum转为带标签的sqlx JSON?
实现枚举到sqlx JSON类型的转换(带变体标签)
要让包含HashMap的枚举像Serde那样以变体名称为标签转换为sqlx的JSON类型,需要结合Serde的序列化规则和sqlx的类型适配trait,具体步骤如下:
1. 添加依赖
确保Cargo.toml中包含必要的依赖:
[dependencies] serde = { version = "1.0", features = ["derive"] } sqlx = { version = "0.7", features = ["postgres", "json", "macros", "runtime-tokio-rustls"] } tokio = { version = "1.0", features = ["full"] }
(如果使用MySQL,将postgres相关特性替换为mysql即可)
2. 定义枚举并配置Serde标签
给枚举添加Serde的派生宏,通过tag属性指定变体名称作为JSON的标签字段,可选content属性定义变体数据的字段名:
use serde::{Serialize, Deserialize}; use std::collections::HashMap; #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type", content = "data")] enum EnumEg { Variant1(HashMap<String, String>), Variant2, }
这样序列化后:
Variant1(HashMap)会输出{"type": "Variant1", "data": {"a": "b"}}Variant2会输出{"type": "Variant2"}
3. 实现sqlx的类型适配trait
为枚举实现sqlx的Type、Encode、Decode trait,让sqlx能将其识别为JSON类型:
use sqlx::{postgres::PgJson, Type, Encode, Decode, Postgres}; // 告诉sqlx该类型对应Postgres的JSONB类型 impl Type<Postgres> for EnumEg { fn type_info() -> sqlx::postgres::PgTypeInfo { PgJson::<Self>::type_info() } fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool { PgJson::<Self>::compatible(ty) } } // 将枚举序列化为JSONB格式 impl<'q> Encode<'q, Postgres> for EnumEg { fn encode_by_ref(&self, buf: &mut sqlx::postgres::PgArgumentBuffer) -> sqlx::encode::IsNull { let json_value = serde_json::to_value(self) .map_err(|e| sqlx::error::BoxDynError::from(e)) .unwrap(); PgJson(json_value).encode_by_ref(buf) } } // 从JSONB反序列化为枚举 impl<'r> Decode<'r, Postgres> for EnumEg { fn decode(value: sqlx::postgres::PgValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> { let pg_json: PgJson<serde_json::Value> = PgJson::decode(value)?; Ok(serde_json::from_value(pg_json.0)?) } }
4. 使用示例
现在你可以直接在sqlx的QueryBuilder中绑定枚举值,和你原来的代码逻辑一致:
const BIND_LIMIT: usize = 65535; #[tokio::main] async fn main() -> Result<(), sqlx::Error> { let mut ha: HashMap<String, String> = HashMap::new(); ha.insert("a".to_string(), "b".to_string()); let items = vec![EnumEg::Variant1(ha)]; let pool = sqlx::postgres::PgPool::connect("postgres://user:pass@localhost/db").await?; let mut query_builder = QueryBuilder::new("INSERT INTO table (item)"); query_builder.push_values( items.into_iter().take(BIND_LIMIT / 2), |mut b, item| { b.push_bind(item); }, ); query_builder.build().execute(&pool).await?; Ok(()) }
注意事项
- 数据库中
item字段的类型需设置为JSONB(Postgres)或JSON(MySQL) - 实际项目中不要使用
unwrap,应该妥善处理serde_json序列化/反序列化的错误 - 如果使用其他数据库,需要对应调整sqlx的类型实现(如MySQL的
MySqlJson等)
内容的提问来源于stack exchange,提问作者toonday
相关产品推荐
相关产品推荐

