<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://blog.kali-team.cn/feed_style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh">
    <tabi:metadata xmlns:tabi="https://github.com/welpo/tabi">
        <tabi:base_url>https:&#x2F;&#x2F;blog.kali-team.cn</tabi:base_url>
        <tabi:separator>
            •
        </tabi:separator>
        <tabi:about_feeds>这是Web Feed，又称为Atom Feed，把现在的网址复制到新闻阅读器即可订阅本站文章。造访「About Feeds」来了解更多资讯。</tabi:about_feeds>
        <tabi:visit_the_site>造访网站</tabi:visit_the_site>
        <tabi:recent_posts>近期文章</tabi:recent_posts>
        <tabi:last_updated_on>更新于 $DATE</tabi:last_updated_on>
        <tabi:default_theme></tabi:default_theme>
        <tabi:post_listing_date>date</tabi:post_listing_date>
        <tabi:current_section>diesel</tabi:current_section>
    </tabi:metadata><link rel="extra-stylesheet" href="https://blog.kali-team.cn/skins/arch.css?h=8b651815fde702215b07" /><title>Kali-Team - diesel</title>
        <subtitle>三米前有蕉皮的博客</subtitle>
    <link href="https://blog.kali-team.cn/tags/diesel/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://blog.kali-team.cn/tags/diesel/" rel="alternate" type="text/html"/>
    <generator uri="https://www.getzola.org/">Zola</generator><updated>2024-02-11T00:00:00+00:00</updated><id>https://blog.kali-team.cn/tags/diesel/atom.xml</id><entry xml:lang="zh">
        <title>diesel自定义类型自动序列化</title>
        <published>2024-02-11T00:00:00+00:00</published>
        <updated>2024-02-11T00:00:00+00:00</updated>
        <author>
            <name>三米前有蕉皮</name>
        </author>
        <link rel="alternate" href="https://blog.kali-team.cn/blog/diesel自定义类型自动序列化/" type="text/html"/>
        <id>https://blog.kali-team.cn/blog/diesel自定义类型自动序列化/</id>
        
            <content type="html">&lt;h1 id=&quot;qian-yan&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#qian-yan&quot; aria-label=&quot;Anchor link for: qian-yan&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
前言&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;在https:&#x2F;&#x2F;github.com&#x2F;emo-crab&#x2F;nvd-rs项目中存在前后端模型用着两种重复的数据结构，在之前写的后端mysql表的json数据保存的&lt;code&gt;serde_json&lt;&#x2F;code&gt;的&lt;code&gt;Value&lt;&#x2F;code&gt;，并不是对应的&lt;code&gt;struct&lt;&#x2F;code&gt;，前端使用的是正常&lt;code&gt;struct&lt;&#x2F;code&gt;，导致重复写了两份代码，今天将这两部分共用的数据结构独立出来作为一个模块。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;xian-zhuang-he-si-lu&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#xian-zhuang-he-si-lu&quot; aria-label=&quot;Anchor link for: xian-zhuang-he-si-lu&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
现状和思路&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;例如现在的CVE结构为&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Properties)]
pub struct Cve {
  pub id: String,
  pub year: i32,
  pub assigner: String,
  pub description: Vec&amp;lt;nvd_cves::v4::Description&amp;gt;,
  pub severity: String,
  pub metrics: nvd_cves::impact::ImpactMetrics,
  pub weaknesses: Vec&amp;lt;nvd_cves::v4::Weaknesses&amp;gt;,
  pub configurations: Vec&amp;lt;nvd_cves::v4::configurations::Node&amp;gt;,
  pub references: Vec&amp;lt;nvd_cves::v4::Reference&amp;gt;,
  pub created_at: NaiveDateTime,
  pub updated_at: NaiveDateTime,
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;可以看出前端的是正常的，后端因为mysql的json只能使用&lt;code&gt;Value&lt;&#x2F;code&gt;类型。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;#[cfg_attr(feature = &amp;quot;openapi&amp;quot;, 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,
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;要想把两种数据结构合并需要解决mysql的数据类型问题，很明显mysql里面没有前端这边的nvd库里的数据类型，按照官方的例子&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;diesel-rs&#x2F;diesel&#x2F;blob&#x2F;master&#x2F;examples&#x2F;postgres&#x2F;custom_types&#x2F;src&#x2F;model.rs&quot;&gt;custom_types&lt;&#x2F;a&gt;和之前的https:&#x2F;&#x2F;github.com&#x2F;diesel-rs&#x2F;diesel&#x2F;issues&#x2F;1783可以知道，要想自定义类型只需要给上面的Cve数据结构实现&lt;code&gt;ToSql&lt;&#x2F;code&gt;和&lt;code&gt;FromSql&lt;&#x2F;code&gt;这两个&lt;code&gt;trait&lt;&#x2F;code&gt;，详细实现可以看https:&#x2F;&#x2F;github.com&#x2F;diesel-rs&#x2F;diesel项目代码中的&lt;code&gt;src&#x2F;serialize.rs&lt;&#x2F;code&gt;和&lt;code&gt;src&#x2F;deserialize.rs&lt;&#x2F;code&gt;这两个文件。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;jie-jue-fang-an&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#jie-jue-fang-an&quot; aria-label=&quot;Anchor link for: jie-jue-fang-an&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
解决方案&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;得到实现&lt;code&gt;ToSql&lt;&#x2F;code&gt;和&lt;code&gt;FromSql&lt;&#x2F;code&gt;这两个&lt;code&gt;trait&lt;&#x2F;code&gt;，就可以按照mysql的json类型录入数据库，但是问题要去每个nvd库中将这些数据类型全部实现这些&lt;code&gt;trait&lt;&#x2F;code&gt;需要引入https:&#x2F;&#x2F;github.com&#x2F;diesel-rs&#x2F;diesel这个依赖，自己写的还好，要是这个库不是自己写的就不能在外部再重新实现，所以需要写一个统一通用的数据类型去兼容全部的json。&lt;&#x2F;li&gt;
&lt;li&gt;创建一个AnyValue在后端使用的时候开启db特性，也就是添加AsExpression和FromSqlRow，数据库类型为Json，将上面nvd库中使用到的数据类型都用泛型&lt;code&gt;T&lt;&#x2F;code&gt;表示。这里需要添加&lt;code&gt;transparent&lt;&#x2F;code&gt;属性，不然在序列化的时候会出现找不到&lt;code&gt;inner&lt;&#x2F;code&gt;字段，&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = &amp;quot;db&amp;quot;, derive(AsExpression, FromSqlRow), diesel(sql_type = Json))]
#[serde(transparent)]
pub struct AnyValue&amp;lt;T: Clone&amp;gt;
where
  T: Clone,
{
  inner: T,
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;添加创建新实例和解引用访问实例的方法，在使用这个数据结构里面的类型的时候可以使用&lt;code&gt;deref&lt;&#x2F;code&gt;方法获取。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;impl&amp;lt;T: Default + for&amp;lt;&amp;#x27;de&amp;gt; serde::Deserialize&amp;lt;&amp;#x27;de&amp;gt; + Clone&amp;gt; AnyValue&amp;lt;T&amp;gt; {
  pub fn new(t: T) -&amp;gt; Self {
    Self { inner: t }
  }
}

impl&amp;lt;T: Default + Clone&amp;gt; Deref for AnyValue&amp;lt;T&amp;gt; {
  type Target = T;

  fn deref(&amp;amp;self) -&amp;gt; &amp;amp;Self::Target {
    &amp;amp;self.inner
  }
}

impl&amp;lt;T: Default + Clone&amp;gt; DerefMut for AnyValue&amp;lt;T&amp;gt; {
  fn deref_mut(&amp;amp;mut self) -&amp;gt; &amp;amp;mut Self::Target {
    &amp;amp;mut self.inner
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;最后实现上面所说的这两个&lt;code&gt;trait&lt;&#x2F;code&gt;，完整代码在项目路径&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;emo-crab&#x2F;nvd-rs&#x2F;blob&#x2F;main&#x2F;nvd-model&#x2F;src&#x2F;types.rs&quot;&gt;nvd-model&#x2F;src&#x2F;types.rs&lt;&#x2F;a&gt;，&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;#[cfg(feature = &amp;quot;db&amp;quot;)]
impl&amp;lt;T: Debug + Clone, DB: Backend&amp;gt; FromSql&amp;lt;Json, DB&amp;gt; for AnyValue&amp;lt;T&amp;gt;
where
  serde_json::Value: FromSql&amp;lt;Json, DB&amp;gt;,
  T: DeserializeOwned,
{
  fn from_sql(bytes: DB::RawValue&amp;lt;&amp;#x27;_&amp;gt;) -&amp;gt; deserialize::Result&amp;lt;Self&amp;gt; {
    let value = &amp;lt;serde_json::Value as FromSql&amp;lt;Json, DB&amp;gt;&amp;gt;::from_sql(bytes)?;
    Ok(serde_json::from_value(value)?)
  }
}

#[cfg(feature = &amp;quot;db&amp;quot;)]
impl&amp;lt;T: Debug + Clone&amp;gt; ToSql&amp;lt;Json, DB&amp;gt; for AnyValue&amp;lt;T&amp;gt;
where
  serde_json::Value: ToSql&amp;lt;Json, DB&amp;gt;,
  T: Serialize,
{
  fn to_sql&amp;lt;&amp;#x27;b&amp;gt;(&amp;amp;&amp;#x27;b self, out: &amp;amp;mut Output&amp;lt;&amp;#x27;b, &amp;#x27;_, DB&amp;gt;) -&amp;gt; serialize::Result {
    let value = serde_json::to_value(&amp;amp;self.inner)?;
    &amp;lt;serde_json::Value as ToSql&amp;lt;Json, DB&amp;gt;&amp;gt;::to_sql(&amp;amp;value, &amp;amp;mut out.reborrow())
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;shi-yong-zi-ding-yi-lei-xing&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#shi-yong-zi-ding-yi-lei-xing&quot; aria-label=&quot;Anchor link for: shi-yong-zi-ding-yi-lei-xing&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
使用自定义类型&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;在回过头来看Cve这个数据结构，将之前的两个合并为一个公共的数据结构，然后使用&lt;code&gt;feature&lt;&#x2F;code&gt;特性条件编译实现不同的属性，例如在前端我会开启&lt;code&gt;yew&lt;&#x2F;code&gt;特性，然后实现一个组件属性功能，在后端开启&lt;code&gt;db&lt;&#x2F;code&gt;特性，开启deisel的属性&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;#[cfg_attr(feature = &amp;quot;openapi&amp;quot;, derive(ToSchema))]
#[cfg_attr(feature = &amp;quot;db&amp;quot;, derive(Queryable, Identifiable), diesel(table_name = cves))]
#[cfg_attr(feature = &amp;quot;yew&amp;quot;, derive(Properties))]
#[derive(Default, Serialize, Clone, Deserialize, Debug, PartialEq)]
pub struct Cve {
  pub id: String,
  pub year: i32,
  pub assigner: String,
  pub description: AnyValue&amp;lt;Vec&amp;lt;nvd_cves::v4::Description&amp;gt;&amp;gt;,
  pub severity: String,
  pub metrics: AnyValue&amp;lt;nvd_cves::impact::ImpactMetrics&amp;gt;,
  pub weaknesses: AnyValue&amp;lt;Vec&amp;lt;nvd_cves::v4::Weaknesses&amp;gt;&amp;gt;,
  pub configurations: AnyValue&amp;lt;Vec&amp;lt;nvd_cves::v4::configurations::Node&amp;gt;&amp;gt;,
  pub references: AnyValue&amp;lt;Vec&amp;lt;nvd_cves::v4::Reference&amp;gt;&amp;gt;,
  pub created_at: NaiveDateTime,
  pub updated_at: NaiveDateTime,
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;在使用AnyValue里面的类型时，只需要解引用就可以，也可以使用&lt;code&gt;deref&lt;&#x2F;code&gt;显式解引用&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&amp;lt;div class=&amp;quot;card-header&amp;quot;&amp;gt;
{self.description(&amp;amp;cve.description)}
	&amp;lt;&amp;#x2F;div&amp;gt;
	  {self.cvss(cve.clone())}
	  {self.references(&amp;amp;cve.references)}
	  {self.weaknesses(&amp;amp;cve.weaknesses)}
	  {self.exploit(cve.id)}
	  {self.configurations(&amp;amp;cve.configurations)}
	  &amp;lt;Comments&amp;#x2F;&amp;gt;
	&amp;lt;div class=&amp;quot;card-body&amp;quot;&amp;gt;
&amp;lt;&amp;#x2F;div&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;zi-dong-xu-lie-hua-uuid&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#zi-dong-xu-lie-hua-uuid&quot; aria-label=&quot;Anchor link for: zi-dong-xu-lie-hua-uuid&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
自动序列化UUID&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;在mysql驱动里没有uuid这个类型，作为代替只用byte类型，在deisel中就都使用&lt;code&gt;Vec[u8]&lt;&#x2F;code&gt;代替，但是前端对字节类型解析会是一段base64编码，所以需要对字段id序列化为前端可以识别的类型。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;#[cfg_attr(feature = &amp;quot;openapi&amp;quot;, derive(ToSchema))]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[cfg_attr(feature = &amp;quot;db&amp;quot;, derive(Queryable, Identifiable, Selectable), diesel(table_name = vendors))]
#[cfg_attr(feature = &amp;quot;yew&amp;quot;, derive(Properties))]
pub struct Vendor {
  #[serde(with = &amp;quot;uuid_serde&amp;quot;)]
  pub id: Vec&amp;lt;u8&amp;gt;,
  pub official: u8,
  pub name: String,
  pub description: Option&amp;lt;String&amp;gt;,
  pub meta: AnyValue&amp;lt;MetaData&amp;gt;,
  pub updated_at: NaiveDateTime,
  pub created_at: NaiveDateTime,
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;使用uuid自身的serde特性创建一个mod，在要序列化字段添加&lt;code&gt;#[serde(with = &quot;uuid_serde&quot;)]&lt;&#x2F;code&gt;，即可做到Vec[u8]和uuid相互转换。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;pub mod uuid_serde {
  use serde::{Deserializer, Serializer};

  pub fn serialize&amp;lt;S: Serializer&amp;gt;(v: &amp;amp;[u8], s: S) -&amp;gt; Result&amp;lt;S::Ok, S::Error&amp;gt; {
    match uuid::Uuid::from_slice(v) {
      Ok(u) =&amp;gt; uuid::serde::compact::serialize(&amp;amp;u, s),
      Err(e) =&amp;gt; Err(serde::ser::Error::custom(e)),
    }
  }

  pub fn deserialize&amp;lt;&amp;#x27;de, D: Deserializer&amp;lt;&amp;#x27;de&amp;gt;&amp;gt;(d: D) -&amp;gt; Result&amp;lt;Vec&amp;lt;u8&amp;gt;, D::Error&amp;gt; {
    match uuid::serde::compact::deserialize(d) {
      Ok(u) =&amp;gt; Ok(u.as_bytes().to_vec()),
      Err(e) =&amp;gt; Err(serde::de::Error::custom(e)),
    }
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;can-kao&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#can-kao&quot; aria-label=&quot;Anchor link for: can-kao&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
参考&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.diesel.rs&#x2F;master&#x2F;diesel&#x2F;sql_types&#x2F;index.html&quot;&gt;https:&#x2F;&#x2F;docs.diesel.rs&#x2F;master&#x2F;diesel&#x2F;sql_types&#x2F;index.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;serde.rs&#x2F;container-attrs.html&quot;&gt;https:&#x2F;&#x2F;serde.rs&#x2F;container-attrs.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        <summary type="html">diesel自定义类型自动序列化</summary>
        </entry>
</feed>
