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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 11:07:53