如何用Sqlx将PostgreSQL的RECORD[]映射为Rust的Vec<_>结构体字段
解决方案
可以不用创建PostgreSQL自定义记录类型,通过JSON序列化嵌套数据的方式实现,这是最便捷的方案:
步骤1:修改SQL查询,用json_agg生成结构化JSON数组
把原来的ARRAY(SELECT ROW(...))替换成json_agg,同时用json_build_object确保JSON键与Rust结构体字段名严格对应:
SELECT accounts.id, accounts.created, accounts.private_key, accounts.public_key, json_agg( json_build_object('email', logins.email, 'oidc_id', logins.oidc_id) ) AS logins FROM accounts LEFT JOIN logins ON logins.account_id = accounts.id GROUP BY accounts.id, accounts.created, accounts.private_key, accounts.public_key
- 用
LEFT JOIN保证无关联登录记录的账户也能被查询到(此时logins字段返回空数组[]) json_build_object生成与Login结构体字段匹配的键值对,避免匿名RECORD默认的f1/f2字段名导致反序列化失败
步骤2:调整Rust代码适配JSON映射
因为你的Login已派生Deserialize,Sqlx可直接将PostgreSQL的json类型映射为Vec<Login>,有两种实现方式:
方式A:在query_as!中显式指定字段类型
无需修改结构体,直接在查询宏里声明logins的目标类型:
let result = sqlx::query_as!( Account, r#" SELECT accounts.id, accounts.created, accounts.private_key, accounts.public_key, json_agg(json_build_object('email', logins.email, 'oidc_id', logins.oidc_id)) AS "logins: Vec<Login>" FROM accounts LEFT JOIN logins ON logins.account_id = accounts.id GROUP BY accounts.id, accounts.created, accounts.private_key, accounts.public_key "# ).fetch_all(pool).await?;
- 通过
"logins: Vec<Login>"的语法,告知Sqlx将该JSON字段反序列化为Vec<Login>
方式B:给结构体字段添加Sqlx类型注解
修改Account结构体的logins字段,标注它对应PostgreSQL的json类型:
use sqlx::types::Json; #[derive(Serialize, Deserialize, sqlx::FromRow)] struct Account { id: Uuid, created: NaiveDateTime, private_key: String, public_key: String, #[sqlx(type = "Json")] logins: Vec<Login> }
之后查询时可省略类型声明,直接使用简化版query_as!:
let result = sqlx::query_as!( Account, r#" SELECT accounts.id, accounts.created, accounts.private_key, accounts.public_key, json_agg(json_build_object('email', logins.email, 'oidc_id', logins.oidc_id)) AS logins FROM accounts LEFT JOIN logins ON logins.account_id = accounts.id GROUP BY accounts.id, accounts.created, accounts.private_key, accounts.public_key "# ).fetch_all(pool).await?;
原方案失效原因
你之前用ARRAY(SELECT ROW(...))返回的是PostgreSQL匿名RECORD数组,这种类型没有明确的结构元数据,Sqlx无法自动映射到Vec<Login>——Sqlx需要明确知道RECORD的字段顺序、类型与结构体的对应关系,而匿名RECORD无法提供这些信息。JSON是结构化数据,只要字段名匹配,就能通过Serde自动完成反序列化。
内容的提问来源于stack exchange,提问作者Achim
相关产品推荐
相关产品推荐

