如何使用 Diesel 在 sqlite 中存储任意 JSON 对象
How to store arbitrary JSON object in sqlite using Diesel
我有一个输入 JSON:
{"key1": "val1", "key2": 1}
我想将它存储在一个 sqlite 数据库中,以便稍后使用完全相同的值响应一些 API 请求。
这是我的迁移:
CREATE TABLE my_table (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
arbitrary_json TEXT NOT NULL
);
我的Cargo.toml
:
[package]
name = "diesel_playground"
version = "0.1.0"
authors = ["User <user@example.com>"]
edition = "2018"
[dependencies]
diesel = { version = "1.4" , features = ["sqlite"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
使用以下代码:
#[macro_use]
extern crate diesel;
mod schema;
use schema::my_table;
use serde::{Deserialize, Serialize};
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use std::env;
pub fn establish_connection() -> SqliteConnection {
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
SqliteConnection::establish(&database_url)
.expect(&format!("Error connecting to {}", database_url))
}
#[derive(Debug, Deserialize, Serialize, Queryable, Identifiable)]
#[table_name = "my_table"]
struct Item {
id: i32,
arbitrary_json: serde_json::Value,
}
#[derive(Debug, Deserialize, Serialize, Insertable, Queryable)]
#[table_name = "my_table"]
struct NewItem {
arbitrary_json: serde_json::Value,
}
fn main() {
let my_json = serde_json::json!({
"key1": "val1",
"key2": 1
});
let new_item = NewItem {
arbitrary_json: my_json,
};
let conn = establish_connection();
diesel::insert_into(my_table::table)
.values(&new_item)
.execute(&conn)
.expect("Error adding new item");
let my_item = my_table::table
.find(1)
.first::<Item>(&conn)
.expect("Error selecting id 1");
assert!(my_item.arbitrary_json == new_item.arbitrary_json);
}
我收到以下错误:
error[E0277]: the trait bound `serde_json::value::Value: diesel::Expression` is not satisfied
--> src/main.rs:27:41
|
27 | #[derive(Debug, Deserialize, Serialize, Insertable, Queryable)]
| ^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `serde_json::value::Value`
我可以创建一个具有 String
JSON 表示的模型,并为 API 输入类型派生 From
/ Into
,但我不想现在在我的代码中到处插入 .into()
。一个 DRY 解决方案就是按照我在所附代码中提出的那样执行此操作。
在我的回答中,我将以字符串表示形式(模式:TEXT)将 JSON 对象保留在数据库中。
对于我们不受支持的类型,我们需要实现以下特征:ToSql
、FromSql
、AsExpression
和 FromSqlRow
。
现在,由于无法为来自外部板条箱的类型实现特征,因此必须将其包装到单个元素元组中:
struct MyJsonType(serde_json::Value)
现在 FromSql
特征实现:
impl FromSql<Text, DB> for MyJsonType {
fn from_sql(
bytes: Option<&<diesel::sqlite::Sqlite as Backend>::RawValue>,
) -> deserialize::Result<Self> {
let t = <String as FromSql<Text, DB>>::from_sql(bytes)?;
Ok(Self(serde_json::from_str(&t)?))
}
}
和ToSql
特征实现:
impl ToSql<Text, DB> for MyJsonType {
fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
let s = serde_json::to_string(&self.0)?;
<String as ToSql<Text, DB>>::to_sql(&s, out)
}
}
现在可以使用宏导出剩余的特征:
#[derive(AsExpression, Debug, Deserialize, Serialize, FromSqlRow)]
#[sql_type = "Text"]
struct MyJsonType(serde_json::Value);
现在可以使用我们的新类型了:
#[derive(Debug, Deserialize, Serialize, Queryable, Identifiable)]
#[table_name = "my_table"]
struct Item {
id: i32,
arbitrary_json: MyJsonType
}
我有一个输入 JSON:
{"key1": "val1", "key2": 1}
我想将它存储在一个 sqlite 数据库中,以便稍后使用完全相同的值响应一些 API 请求。
这是我的迁移:
CREATE TABLE my_table (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
arbitrary_json TEXT NOT NULL
);
我的Cargo.toml
:
[package]
name = "diesel_playground"
version = "0.1.0"
authors = ["User <user@example.com>"]
edition = "2018"
[dependencies]
diesel = { version = "1.4" , features = ["sqlite"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
使用以下代码:
#[macro_use]
extern crate diesel;
mod schema;
use schema::my_table;
use serde::{Deserialize, Serialize};
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use std::env;
pub fn establish_connection() -> SqliteConnection {
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
SqliteConnection::establish(&database_url)
.expect(&format!("Error connecting to {}", database_url))
}
#[derive(Debug, Deserialize, Serialize, Queryable, Identifiable)]
#[table_name = "my_table"]
struct Item {
id: i32,
arbitrary_json: serde_json::Value,
}
#[derive(Debug, Deserialize, Serialize, Insertable, Queryable)]
#[table_name = "my_table"]
struct NewItem {
arbitrary_json: serde_json::Value,
}
fn main() {
let my_json = serde_json::json!({
"key1": "val1",
"key2": 1
});
let new_item = NewItem {
arbitrary_json: my_json,
};
let conn = establish_connection();
diesel::insert_into(my_table::table)
.values(&new_item)
.execute(&conn)
.expect("Error adding new item");
let my_item = my_table::table
.find(1)
.first::<Item>(&conn)
.expect("Error selecting id 1");
assert!(my_item.arbitrary_json == new_item.arbitrary_json);
}
我收到以下错误:
error[E0277]: the trait bound `serde_json::value::Value: diesel::Expression` is not satisfied
--> src/main.rs:27:41
|
27 | #[derive(Debug, Deserialize, Serialize, Insertable, Queryable)]
| ^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `serde_json::value::Value`
我可以创建一个具有 String
JSON 表示的模型,并为 API 输入类型派生 From
/ Into
,但我不想现在在我的代码中到处插入 .into()
。一个 DRY 解决方案就是按照我在所附代码中提出的那样执行此操作。
在我的回答中,我将以字符串表示形式(模式:TEXT)将 JSON 对象保留在数据库中。
对于我们不受支持的类型,我们需要实现以下特征:ToSql
、FromSql
、AsExpression
和 FromSqlRow
。
现在,由于无法为来自外部板条箱的类型实现特征,因此必须将其包装到单个元素元组中:
struct MyJsonType(serde_json::Value)
现在 FromSql
特征实现:
impl FromSql<Text, DB> for MyJsonType {
fn from_sql(
bytes: Option<&<diesel::sqlite::Sqlite as Backend>::RawValue>,
) -> deserialize::Result<Self> {
let t = <String as FromSql<Text, DB>>::from_sql(bytes)?;
Ok(Self(serde_json::from_str(&t)?))
}
}
和ToSql
特征实现:
impl ToSql<Text, DB> for MyJsonType {
fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
let s = serde_json::to_string(&self.0)?;
<String as ToSql<Text, DB>>::to_sql(&s, out)
}
}
现在可以使用宏导出剩余的特征:
#[derive(AsExpression, Debug, Deserialize, Serialize, FromSqlRow)]
#[sql_type = "Text"]
struct MyJsonType(serde_json::Value);
现在可以使用我们的新类型了:
#[derive(Debug, Deserialize, Serialize, Queryable, Identifiable)]
#[table_name = "my_table"]
struct Item {
id: i32,
arbitrary_json: MyJsonType
}