rust - 如何使用 Diesel 在 sqlite 中存储任意 JSON 对象

标签 rust rust-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 对象保存在数据库中。

对于不受支持的类型,我们需要实现以下特征:ToSqlFromSqlAsExpressionFromSqlRow

现在,由于无法为来自外部 crate 的类型实现特征,因此必须将其包装到单个元素元组中:

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 trait 实现:

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
}

关于rust - 如何使用 Diesel 在 sqlite 中存储任意 JSON 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56495152/

相关文章:

rust - 在Rust中使用单个增变器进行无锁数据共享

rust - 为类型构造函数推断类型

rust - 为什么 Diesel 结构中的可选字段未实现特征

rust - 什么是元组变体? (了解编译器错误信息)

pointers - std::ptr::Unique 没有名为 offset 的方法

rust - 如何解决在当前范围内为可变引用 “no method named `找到的 `&mut Bar<X>` foo`”

asynchronous - 始终返回 Ok HttpResponse 然后在 actix-web 处理程序中工作

multithreading - web::block() 失败并显示 "` NonNull<pq_sys::pg_conn >` cannot be shared between threads safely"

linq - C# Linq "join"在 Rust 中的等价物是什么?

assembly - 如何在(发布的)Rust版本中找到函数的汇编代码?