diesel自定义类型自动序列化
diesel自定义类型自动序列化 前言
serde_json的Value,并不是对应的struct,前端使用的是正常struct,导致重复写了两份代码,今天将这两部分共用的数据结构独立出来作为一个模块。 现状和思路
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Properties)]
pub struct Cve {
pub id: String,
pub year: i32,
pub assigner: String,
pub description: Vec<nvd_cves::v4::Description>,
pub severity: String,
pub metrics: nvd_cves::impact::ImpactMetrics,
pub weaknesses: Vec<nvd_cves::v4::Weaknesses>,
pub configurations: Vec<nvd_cves::v4::configurations::Node>,
pub references: Vec<nvd_cves::v4::Reference>,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
}
Value类型。#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Queryable, Serialize, Deserialize, Identifiable, Debug, PartialEq)]
#[diesel(table_name = cves)]
pub struct Cve {
pub id: String,
pub year: i32,
pub assigner: String,
pub description: Value,
pub severity: String,
pub metrics: Value,
pub weaknesses: Value,
pub configurations: Value,
pub references: Value,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
}
ToSql和FromSql这两个trait,详细实现可以看https://github.com/diesel-rs/diesel项目代码中的src/serialize.rs和src/deserialize.rs这两个文件。 解决方案
ToSql和FromSql这两个trait,就可以按照mysql的json类型录入数据库,但是问题要去每个nvd库中将这些数据类型全部实现这些trait需要引入https://github.com/diesel-rs/diesel这个依赖,自己写的还好,要是这个库不是自己写的就不能在外部再重新实现,所以需要写一个统一通用的数据类型去兼容全部的json。T表示。这里需要添加transparent属性,不然在序列化的时候会出现找不到inner字段,#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "db", derive(AsExpression, FromSqlRow), diesel(sql_type = Json))]
#[serde(transparent)]
pub struct AnyValue<T: Clone>
where
T: Clone,
{
inner: T,
}
deref方法获取。impl<T: Default + for<'de> serde::Deserialize<'de> + Clone> AnyValue<T> {
pub fn new(t: T) -> Self {
Self { inner: t }
}
}
impl<T: Default + Clone> Deref for AnyValue<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: Default + Clone> DerefMut for AnyValue<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
trait,完整代码在项目路径nvd-model/src/types.rs,#[cfg(feature = "db")]
impl<T: Debug + Clone, DB: Backend> FromSql<Json, DB> for AnyValue<T>
where
serde_json::Value: FromSql<Json, DB>,
T: DeserializeOwned,
{
fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
let value = <serde_json::Value as FromSql<Json, DB>>::from_sql(bytes)?;
Ok(serde_json::from_value(value)?)
}
}
#[cfg(feature = "db")]
impl<T: Debug + Clone> ToSql<Json, DB> for AnyValue<T>
where
serde_json::Value: ToSql<Json, DB>,
T: Serialize,
{
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
let value = serde_json::to_value(&self.inner)?;
<serde_json::Value as ToSql<Json, DB>>::to_sql(&value, &mut out.reborrow())
}
}
使用自定义类型
feature特性条件编译实现不同的属性,例如在前端我会开启yew特性,然后实现一个组件属性功能,在后端开启db特性,开启deisel的属性#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[cfg_attr(feature = "db", derive(Queryable, Identifiable), diesel(table_name = cves))]
#[cfg_attr(feature = "yew", derive(Properties))]
#[derive(Default, Serialize, Clone, Deserialize, Debug, PartialEq)]
pub struct Cve {
pub id: String,
pub year: i32,
pub assigner: String,
pub description: AnyValue<Vec<nvd_cves::v4::Description>>,
pub severity: String,
pub metrics: AnyValue<nvd_cves::impact::ImpactMetrics>,
pub weaknesses: AnyValue<Vec<nvd_cves::v4::Weaknesses>>,
pub configurations: AnyValue<Vec<nvd_cves::v4::configurations::Node>>,
pub references: AnyValue<Vec<nvd_cves::v4::Reference>>,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
}
deref显式解引用<div class="card-header">
{self.description(&cve.description)}
</div>
{self.cvss(cve.clone())}
{self.references(&cve.references)}
{self.weaknesses(&cve.weaknesses)}
{self.exploit(cve.id)}
{self.configurations(&cve.configurations)}
<Comments/>
<div class="card-body">
</div>
自动序列化UUID
Vec[u8]代替,但是前端对字节类型解析会是一段base64编码,所以需要对字段id序列化为前端可以识别的类型。#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "db", derive(Queryable, Identifiable, Selectable), diesel(table_name = vendors))]
#[cfg_attr(feature = "yew", derive(Properties))]
pub struct Vendor {
#[serde(with = "uuid_serde")]
pub id: Vec<u8>,
pub official: u8,
pub name: String,
pub description: Option<String>,
pub meta: AnyValue<MetaData>,
pub updated_at: NaiveDateTime,
pub created_at: NaiveDateTime,
}
#[serde(with = "uuid_serde")],即可做到Vec[u8]和uuid相互转换。pub mod uuid_serde {
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(v: &[u8], s: S) -> Result<S::Ok, S::Error> {
match uuid::Uuid::from_slice(v) {
Ok(u) => uuid::serde::compact::serialize(&u, s),
Err(e) => Err(serde::ser::Error::custom(e)),
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
match uuid::serde::compact::deserialize(d) {
Ok(u) => Ok(u.as_bytes().to_vec()),
Err(e) => Err(serde::de::Error::custom(e)),
}
}
}
参考