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

能否像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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 03:16:16