使用serde_json序列化Diesel查询结果时出现多余\r\n字符问题
问题根源分析
这个问题的核心是数据库中存储的username和password字段本身就包含了\r(回车符)或\r\n(回车换行符)控制字符,只是你直接打印的时候,终端会自动处理这些控制字符(比如\r会让光标回到行首,覆盖后续输出的部分内容),导致你看起来数据是正常的;但serde_json在序列化时会忠实地保留所有原始字符,并将控制字符转义为\r、\r\n的形式输出。
你可以通过数据库客户端直接验证这一点:比如执行类似下面的SQL(以PostgreSQL为例),把字段和一个可见的分隔符拼接,就能看到隐藏的控制字符:
SELECT id, username || '---', password || '---' FROM users WHERE id = 4;
执行后你会发现username或password的末尾会带有\r或\r\n的痕迹。
解决方案
针对这个问题,你可以从「清理现有数据」和「代码层面处理」两个角度入手,同时做好预防措施避免后续再出现类似问题:
1. 清理数据库中的现有数据
如果这些控制字符是不必要的脏数据,直接通过SQL更新数据库记录即可。不同数据库的处理函数略有不同:
- PostgreSQL:
UPDATE users SET username = TRIM(BOTH '\r\n' FROM username), password = TRIM(BOTH '\r\n' FROM password) WHERE id = 4; -- 可以根据需要调整WHERE条件批量处理 - MySQL:
UPDATE users SET username = TRIM(TRAILING '\r\n' FROM username), password = TRIM(TRAILING '\r\n' FROM password) WHERE id = 4;
2. 在代码层面自动清理数据
如果无法直接修改数据库,或者想在数据加载后统一处理,可以在Rust代码中对字段进行清理:
方式一:查询后直接处理每条记录
在加载数据后,遍历并清理每个用户的字段:
let mut users = users .filter(id.eq(p_id)) .limit(10) .load::<Users>(&connection) .expect("Error loading posts"); // 清理字段中的换行/回车控制字符 for user in &mut users { // trim_matches会移除字符串首尾的指定字符 user.username = user.username.trim_matches(&['\r', '\n'][..]).to_string(); user.password = user.password.trim_matches(&['\r', '\n'][..]).to_string(); }
方式二:自定义Serde序列化逻辑
如果希望每次序列化Users时自动清理字段,可以实现自定义的Serialize trait,避免重复编写清理代码:
use serde::{Serialize, Serializer}; #[derive(Queryable, Deserialize)] pub struct Users { pub id: i32, pub username: String, pub password: String, } impl Serialize for Users { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { // 定义临时结构体用于序列化,自动清理字段 #[derive(Serialize)] struct CleanedUser<'a> { id: i32, username: &'a str, password: &'a str, } let cleaned_user = CleanedUser { id: self.id, username: self.username.trim_matches(&['\r', '\n'][..]), password: self.password.trim_matches(&['\r', '\n'][..]), }; cleaned_user.serialize(serializer) } }
3. 预防措施:从数据入库环节控制
为了避免后续再出现脏数据,建议在数据入库前(比如用户注册、更新信息时)就对输入的字符串进行清理:
// 示例:接收前端请求的DTO #[derive(Deserialize)] pub struct CreateUserRequest { pub username: String, pub password: String, } impl CreateUserRequest { // 添加清理方法,处理后再用于入库 pub fn sanitize(self) -> Self { CreateUserRequest { username: self.username.trim_matches(&['\r', '\n'][..]).to_string(), password: self.password.trim_matches(&['\r', '\n'][..]).to_string(), } } } // 入库时使用清理后的数据 let request = req.json().await?.sanitize(); let new_user = NewUser { username: request.username, password: request.password, }; diesel::insert_into(users::table) .values(&new_user) .execute(&connection) .expect("Failed to create user");
内容的提问来源于stack exchange,提问作者Stuhl
相关产品推荐
相关产品推荐

