SQLx将JsonValue字段始终识别为字符串,如何解决?
问题解决:Sqlx解析Sqlite VARCHAR字段为JsonValue::String而非JSON对象的问题
问题原因
Sqlite本身没有原生JSON类型,sqlx默认会将VARCHAR字段读取为普通字符串,即使存储的内容是JSON格式的字符串,也会直接包装成JsonValue::String,不会自动解析为JSON结构。
解决方案
方案一:查询时用Sqlite JSON函数显式解析
如果你的Sqlite版本≥3.38.0(该版本引入了json()函数),可以在查询语句中直接调用json()函数将字符串转换为JSON对象,让sqlx正确识别并解析为对应类型:
let foo = sqlx::query_as!(Foo, "SELECT json(bar) AS bar FROM foo LIMIT 1") .fetch_one(&db) .await?;
执行后foo.bar会被解析为Object {},符合预期。
方案二:自定义类型实现数据库转换逻辑
如果不想每次查询都写json(),可以封装一个自定义类型,手动实现从字符串到JsonValue的解析:
use sqlx::{FromSql, Sqlite, SqliteValue}; use serde_json::Value as JsonValue; #[derive(Debug, Clone, PartialEq)] pub struct DbJson(pub JsonValue); // 实现从Sqlite值转换为DbJson impl FromSql<Sqlite> for DbJson { fn from_sql(value: SqliteValue<'_>) -> sqlx::Result<Self> { let s = String::from_sql(value)?; let json = serde_json::from_str(&s) .map_err(|e| sqlx::Error::Decode(e.into()))?; Ok(DbJson(json)) } } // 可选:实现写入数据库的逻辑 impl sqlx::ToSql<Sqlite> for DbJson { fn to_sql(&self) -> sqlx::Result<sqlx::sqlite::SqliteArgumentValue<'_>> { let s = serde_json::to_string(&self.0) .map_err(|e| sqlx::Error::Encode(e.into()))?; Ok(s.into()) } }
修改结构体使用自定义类型:
#[derive(sqlx::FromRow)] pub struct Foo { pub bar: DbJson, }
使用时通过foo.bar.0访问解析后的JsonValue。
方案三:通过字段属性手动转换
在结构体字段上添加#[sqlx(try_from = "String")]属性,同时为JsonValue实现TryFrom<String>:
use std::convert::TryFrom; use serde_json::Error as JsonError; // 为JsonValue实现从String的尝试转换 impl TryFrom<String> for sqlx::types::JsonValue { type Error = JsonError; fn try_from(s: String) -> Result<Self, Self::Error> { serde_json::from_str(&s) } }
修改结构体字段:
#[derive(sqlx::FromRow)] pub struct Foo { #[sqlx(try_from = "String")] pub bar: sqlx::types::JsonValue, }
查询时sqlx会自动尝试将字符串解析为JsonValue,解析失败会返回Decode错误。
内容的提问来源于stack exchange,提问作者kaya3
相关产品推荐
相关产品推荐

