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

如何在不依赖serde的domain crate外序列化其定义的struct?

解决方案

方案1:用serde Remote Derive手动实现序列化逻辑

无需修改domain crate,借助serde的remote属性为外部类型生成序列化代码:

  1. 在依赖domain的crate中,定义与domain::Project字段完全一致的本地结构体,添加#[serde(remote = "domain::Project")]标记:
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
#[serde(remote = "domain::Project")]
struct ProjectRemote {
    pub id: u32,
    pub name: String,
    // 复制domain::Project的所有字段
}
  1. 为domain::Project手动实现Serialize和Deserialize trait,通过ProjectRemote完成字段映射:
impl Serialize for domain::Project {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        ProjectRemote {
            id: self.id,
            name: self.name.clone(),
            // 一一映射所有字段
        }.serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for domain::Project {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let remote = ProjectRemote::deserialize(deserializer)?;
        Ok(domain::Project {
            id: remote.id,
            name: remote.name,
            // 反向映射所有字段
        })
    }
}
  1. 此时ProjectDto即可正常派生序列化特性:
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProjectDto {
    pub project: domain::Project,
}

方案2:用serde_with库简化实现

serde_with的RemoteDerive宏可以自动生成上述手动代码,减少重复劳动:

  1. 在Cargo.toml中添加依赖:
serde_with = "3.0"
  1. 在依赖crate中定义匹配字段的本地结构体,使用RemoteDerive宏:
use serde_with::RemoteDerive;
use serde::{Serialize, Deserialize};

#[derive(RemoteDerive, Serialize, Deserialize)]
#[remote(domain::Project)]
struct ProjectRemote {
    pub id: u32,
    pub name: String,
    // 复制domain::Project的所有字段
}
  1. 宏会自动为domain::Project生成序列化/反序列化实现,后续ProjectDto可直接使用。

方案3:优化你当前的复制+映射方案

如果倾向保留现有思路,可通过From trait自动完成类型转换,减少手动维护成本:

  1. 定义带序列化标记的本地结构体:
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProjectDtoInner {
    pub id: u32,
    pub name: String,
    // 与domain::Project字段完全一致
}
  1. 实现双向转换的From trait:
impl From<domain::Project> for ProjectDtoInner {
    fn from(project: domain::Project) -> Self {
        ProjectDtoInner {
            id: project.id,
            name: project.name,
        }
    }
}

impl From<ProjectDtoInner> for domain::Project {
    fn from(dto_inner: ProjectDtoInner) -> Self {
        domain::Project {
            id: dto_inner.id,
            name: dto_inner.name,
        }
    }
}
  1. 修改ProjectDto使用转换逻辑:
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProjectDto {
    #[serde(serialize_with = "serialize_project", deserialize_with = "deserialize_project")]
    pub project: domain::Project,
}

fn serialize_project<S>(project: &domain::Project, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    ProjectDtoInner::from(project.clone()).serialize(serializer)
}

fn deserialize_project<'de, D>(deserializer: D) -> Result<domain::Project, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let dto_inner = ProjectDtoInner::deserialize(deserializer)?;
    Ok(domain::Project::from(dto_inner))
}

报错回顾

你遇到的核心问题是serde要求结构体所有字段都实现Serialize/Deserialize,而domain::Project未满足该约束:

error[E0277]: the trait bound Project: Serialize is not satisfied
--> src/project_dto.rs:6:39
|
6 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
| ^^^^^^^^^ the trait Serialize is not
| implemented for Project
7 | pub struct ProjectDto {
8 | pub project: Project,
| --- required by a bound introduced by this call

内容的提问来源于stack exchange,提问作者A Bit of Help

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 22:05:34