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

如何移除Serde序列化结构体时生成的None/null字段?

问题描述

我正在使用Rust从MongoDB查询用户信息,查询代码如下:

pub async fn find_user_by_name(&self, data: String) -> Vec<Uye> {
    let filter = doc! {"isim": data};
    let options = FindOptions::builder()
        .projection(doc! {"sifre": 0, "mail": 0, "tokenInfo": 0 })
        .build();
    let cursor = match self.user_col.find(None, options).await {
        std::result::Result::Ok(cursor) => cursor,
        std::result::Result::Err(_) => return vec![],
    };

    cursor.try_collect().await.unwrap()
}

Uye结构体定义:

pub struct Uye {
    pub id: Option<i32>,
    pub isim: Option<String>,
    pub sifre: Option<String>,
    pub mail: Option<String>,
    pub dogumtarihi: Option<String>,
    pub avatar_url: Option<String>,
    pub kaydedilen: Option<Vec<i32>>,
    pub perm: Option<String>,
    pub permLevel: Option<i32>,
    pub onaylandi: Option<bool>,
    pub isDummy: Option<bool>,
    pub hesap_onay_kodu: Option<i32>,
    pub avatar_path: Option<String>,
    pub tokenInfo: Option<TokenInfo>,
}

当前API返回的JSON数据中存在null字段(如sifre、mail),我希望移除这些null字段优化输出格式,请问有哪些实现方法?

返回的JSON示例:

{
  "status": "basarili",
  "user": [
    {
      "id": 60,
      "isim": "test User",
      "sifre": null,
      "mail": null,
      "dogumtarihi": "",
      "avatar_url": "/static/uploads/images/avatar",
      "kaydedilen": [],
      "perm": "Üye",
      "permLevel": 0,
      "onaylandi": false,
      "isDummy": false,
      "hesap_onay_kodu": 123123,
      "avatar_path": "/static/uploads/images/avatar",
      "tokenInfo": null
    }
  ]
}

解决方案

方法1:利用Serde序列化属性过滤(推荐)

如果你的Uye结构体已经使用了Serde的Serialize派生(和MongoDB交互通常会用到),可以给每个Option类型的字段添加#[serde(skip_serializing_if = "Option::is_none")]属性,序列化JSON时会自动跳过值为None的字段,不会生成null。

修改后的结构体示例:

#[derive(Debug, Serialize, Deserialize)] // 确保已添加Serialize派生
pub struct Uye {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub isim: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sifre: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mail: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dogumtarihi: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub kaydedilen: Option<Vec<i32>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub perm: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub permLevel: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub onaylandi: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub isDummy: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hesap_onay_kodu: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_path: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tokenInfo: Option<TokenInfo>,
}

这样当你把查询到的Vec<Uye>序列化为JSON时,所有None的字段都会被自动忽略,不会出现在输出中。

方法2:定义专用的响应DTO结构体

创建一个只包含需要返回字段的DTO(数据传输对象)结构体,字段类型使用非Option类型,然后从Uye转换为该DTO,过滤掉空值。

示例:

// 定义响应专用结构体
#[derive(Serialize)]
pub struct UyeResponse {
    pub id: i32,
    pub isim: String,
    pub dogumtarihi: String,
    pub avatar_url: String,
    pub kaydedilen: Vec<i32>,
    pub perm: String,
    pub permLevel: i32,
    pub onaylandi: bool,
    pub isDummy: bool,
    pub hesap_onay_kodu: i32,
    pub avatar_path: String,
}

// 实现从Uye到UyeResponse的转换
impl From<Uye> for UyeResponse {
    fn from(uye: Uye) -> Self {
        UyeResponse {
            id: uye.id.unwrap_or_default(), // 根据业务逻辑设置默认值
            isim: uye.isim.unwrap_or_default(),
            dogumtarihi: uye.dogumtarihi.unwrap_or_default(),
            avatar_url: uye.avatar_url.unwrap_or_default(),
            kaydedilen: uye.kaydedilen.unwrap_or_default(),
            perm: uye.perm.unwrap_or_default(),
            permLevel: uye.permLevel.unwrap_or_default(),
            onaylandi: uye.onaylandi.unwrap_or_default(),
            isDummy: uye.isDummy.unwrap_or_default(),
            hesap_onay_kodu: uye.hesap_onay_kodu.unwrap_or_default(),
            avatar_path: uye.avatar_path.unwrap_or_default(),
        }
    }
}

然后修改查询代码,转换为DTO后返回:

pub async fn find_user_by_name(&self, data: String) -> Vec<UyeResponse> {
    let filter = doc! {"isim": data};
    let options = FindOptions::builder()
        .projection(doc! {"sifre": 0, "mail": 0, "tokenInfo": 0 })
        .build();
    let cursor = match self.user_col.find(filter, options).await { // 修正原代码漏传filter的问题
        Ok(cursor) => cursor,
        Err(_) => return vec![],
    };

    let users: Vec<Uye> = cursor.try_collect().await.unwrap_or_default();
    users.into_iter().map(UyeResponse::from).collect()
}

方法3:手动构建JSON对象

如果不想修改结构体或定义新结构体,可以手动构建JSON对象,只添加非空字段:

use serde_json::{json, Value, Map};

pub async fn find_user_by_name(&self, data: String) -> Vec<Value> {
    let filter = doc! {"isim": data};
    let options = FindOptions::builder()
        .projection(doc! {"sifre": 0, "mail": 0, "tokenInfo": 0 })
        .build();
    let cursor = match self.user_col.find(filter, options).await {
        Ok(cursor) => cursor,
        Err(_) => return vec![],
    };

    let users: Vec<Uye> = cursor.try_collect().await.unwrap_or_default();
    users.into_iter().map(|user| {
        let mut obj = Map::new();
        if let Some(id) = user.id {
            obj.insert("id".into(), json!(id));
        }
        if let Some(isim) = user.isim {
            obj.insert("isim".into(), json!(isim));
        }
        if let Some(dogumtarihi) = user.dogumtarihi {
            obj.insert("dogumtarihi".into(), json!(dogumtarihi));
        }
        if let Some(avatar_url) = user.avatar_url {
            obj.insert("avatar_url".into(), json!(avatar_url));
        }
        if let Some(kaydedilen) = user.kaydedilen {
            obj.insert("kaydedilen".into(), json!(kaydedilen));
        }
        if let Some(perm) = user.perm {
            obj.insert("perm".into(), json!(perm));
        }
        if let Some(permLevel) = user.permLevel {
            obj.insert("permLevel".into(), json!(permLevel));
        }
        if let Some(onaylandi) = user.onaylandi {
            obj.insert("onaylandi".into(), json!(onaylandi));
        }
        if let Some(isDummy) = user.isDummy {
            obj.insert("isDummy".into(), json!(isDummy));
        }
        if let Some(hesap_onay_kodu) = user.hesap_onay_kodu {
            obj.insert("hesap_onay_kodu".into(), json!(hesap_onay_kodu));
        }
        if let Some(avatar_path) = user.avatar_path {
            obj.insert("avatar_path".into(), json!(avatar_path));
        }
        Value::Object(obj)
    }).collect()
}

这个方法比较繁琐,但灵活性高,适合临时处理少量字段的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 05:37:10