如何使用 serde 将结构序列化为另一个 Rust 数据结构?

How can I use serde to serialize a struct to another Rust data structure?

我有一个数据结构 Document,我想将其他 Rust 结构序列化到它。它基本上是内部字段的 HashMap,但是它与数据库交互 API,所以我肯定想将其他类型转换为那些 Documents。

例如这个结构

struct Entry {
    id: String,
    user: String,
    duration: u32,
    location: (f64, f64),
}

我已经使用 From 特征转换为 Document 类型,但是当 Entry 结构更改时,这是我必须修改的额外位置。该实现使用 DocumentBuilder 并且看起来像这样:

impl From<Entry> for Document {
    fn from(entry: Entry) -> Self {
        Document::builder()
            .name(&entry.id)           // set the name of the document
            .field("user", entry.user) // add fields ...
            .field("duration", entry.duration)
            .field("location", entry.location)
            .build()                   // build a Document

    }
}

field 方法可以将任何可以转换为 FieldValue 的值分配给键。所以field的签名是:

impl DocumentBuilder {
    // ...
    pub fn field<T: Into<FieldValue>>(mut self, key: &str, value: T) -> Self { ... }
    // ...
}

我想使用 serde 及其派生功能自动将结构及其字段序列化为 Document。我该怎么做呢?我在 wiki 上查看了 Implementing a Serializer,但显示的示例写入了一个字符串,我想知道如何使用构建器模式序列化为数据结构。

最简单的方法是使用 serde_json::from_value(即使您不使用 JSON 也适用,但要求所有字段都有效 JSON [例如,不哈希图中的非字符串键]):

let entry = Entry {
    a: 24,
    b: 42,
    c: "nice".to_string()
};
let v = serde_json::to_value(&entry).unwrap();
let document: Document = serde_json::from_value(v).unwrap();

警告:Document 的值类型必须实现 Deserialize,并且可以将任何值反序列化为正确的参数。这可以通过使用 #[serde(untagged)] 来完成,但可能容易出现某些类型错误,例如 u8 被转换为 u64.

Full playground example

一种不涉及任何不必要副本的更复杂的方法将要求您编写自定义(反)序列化程序,一个值得关注的方法是 serde_transcode::transcode,它执行与你想要的东西 - 它在两种数据格式之间转换。