如何移除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
相关产品推荐
相关产品推荐

