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

如何为Rust结构体添加可空JSON字段并从YAML加载(Diesel兼容)

解决Diesel中JSON字段与Serde反序列化兼容问题

问题背景

需要将包含嵌套metadata的YAML配置加载到带有Insertable、serde::Deserialize、Debug派生宏的CampaignInsertable结构体(用于Diesel数据库操作),添加Option<serde_json::Value>类型字段时,出现Diesel的Expression trait未实现错误。

YAML配置示例

campaign:
  name: Welcome Campaign
  type: new
  start_date: 08/12/2024
  end_date: 10/12/2024
  metadata:
    channel: sms

原结构体代码

#[derive(Insertable, serde::Deserialize, Debug)]
#[diesel(table_name = campaigns)]
pub struct CampaignInsertable {
    #[serde(rename = "name")]
    pub cpg_name: String,
    #[serde(rename = "type")]
    pub cpg_type: String,
    #[serde(with = "date_format")]
    pub start_date: chrono::NaiveDateTime,
    #[serde(with = "date_format")]
    pub end_date: chrono::NaiveDateTime,
    // 需要添加metadata字段
}

尝试添加的字段及报错

添加的字段代码:

#[serde(rename = "metadata")]
pub meta_data: Option<serde_json::Value>,

报错信息:

error[E0277]: the trait bound `serde_json::Value: diesel::Expression` is not satisfied
  --> src/models/campaigns.rs:22:10
   |
22 | #[derive(Insertable, serde::Deserialize, Debug)]
   |          ^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `serde_json::Value`, which is required by `serde_json::Value: AsExpression<diesel::sql_types::Nullable<diesel::sql_types::Jsonb>>`

解决方案

1. 启用Diesel的JSON相关特性

在Cargo.toml中,为Diesel开启对应数据库的JSON特性(以PostgreSQL的jsonb为例):

diesel = { version = "2.1", features = ["postgres", "chrono", "serde_json"] }

2. 使用Diesel的Json类型包装serde_json::Value

Diesel提供了专门的包装类型diesel::serde_json::Json,它实现了Diesel所需的Expression等trait,同时兼容Serde的序列化/反序列化。修改后的结构体代码:

use diesel::sql_types::Jsonb;
use serde_json::Value as JsonValue;

#[derive(Insertable, serde::Deserialize, Debug)]
#[diesel(table_name = campaigns)]
pub struct CampaignInsertable {
    #[serde(rename = "name")]
    pub cpg_name: String,
    #[serde(rename = "type")]
    pub cpg_type: String,
    #[serde(with = "date_format")]
    pub start_date: chrono::NaiveDateTime,
    #[serde(with = "date_format")]
    pub end_date: chrono::NaiveDateTime,
    #[serde(rename = "metadata")]
    #[diesel(sql_type = Jsonb)]
    pub meta_data: Option<diesel::serde_json::Json<JsonValue>>,
}

3. 确保数据库表字段类型匹配

你的campaigns表中meta_data字段需定义为对应数据库的JSON类型(以PostgreSQL为例):

CREATE TABLE campaigns (
    -- 其他字段定义...
    meta_data JSONB
);

原理说明

原生serde_json::Value未实现Diesel的Expression trait,导致Insertable派生宏报错。通过diesel::serde_json::Json包装后,Diesel为该类型实现了所有数据库交互所需的trait,同时它能自动与Serde兼容,直接完成YAML中metadata字段的反序列化。

内容的提问来源于stack exchange,提问作者Fizi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 04:55:11