serialization - 如何将 BSON 反序列化为通用对象?

标签 serialization rust deserialization serde

我正在使用 Serde 将 BSON 对象反序列化为 Rust 结构实例。我可以将对象反序列化为具体的结构实例,但是我该如何反序列化呢?

我在 MongoDB 中有“国家”和“城市”集合。在 Rust 程序中,我有一个用于 CountryCity 的结构。当我从 Mongo 中提取一个国家或城市时,我可以使用 Serde 将其反序列化为 CountryCity 结构。请参阅下面 main() 中的第二行。

我想将 BSON 对象反序列化为通用 Location 对象。根据我在 Rust 书中读到的关于泛型的内容,我创建了一个特征 LocationTrait 并为 CountryCity 实现了它。 (参见 main() 中的第 3 行)。它无法编译,说 dyn LocationTrait 类型值的大小在编译时无法得知。

#[derive(Serialize, Deserialize)]
pub struct Country {
    pub name: String,
}

#[derive(Serialize, Deserialize)]
pub struct City {
    pub name: String,
}

pub trait LocationTrait {}
impl LocationTrait for Country {}
impl LocationTrait for City {}

fn main() {
    let item = mongo_coll
        .find_one(Some(doc! {"name": "usa"}), None)
        .unwrap()
        .unwrap();
    let country: Country = bson::from_bson(bson::Bson::Document(item)).unwrap();
    // fails -> let gen_location: LocationTrait = bson::from_bson(bson::Bson::Document(item)).unwrap();
}

最后,我想创建一个代表CountryCity 的通用对象。但是,我不确定起点——我需要专注于特征还是需要创建一个新的特征绑定(bind)结构?

最佳答案

有两个问题阻止您的代码编译。

您看到的第一个错误:编译时无法知道 dyn LocationTrait 类型值的大小,这是因为 bson::from_bson 需要按值返回反序列化的结果。编译器需要知道它需要在调用堆栈中分配多少空间才能返回它。

然而,特征是描述行为而非数据的抽象,因此它可以为 u8(单个字节)或更大的结构实现。

为了能够返回这样的值,您需要将其装箱(参见 Trait Objects)。

第二个问题是返回值必须实现Deserialize特征(而不是 LocationTrait)

解决这些问题:

最简单的方法是使用枚举而不是特征:

#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Location {
    Country(Country),
    City(City)
}

这将适用于 {"type"= "Country", name="usa"} 等文档。 检查the Serde doc更多选项。

如果您真的想要使用特征(例如,能够在该模块之外定义类型),您将需要一个盒装特征和一个自定义结构:

// The same trait as defined earlier
pub trait LocationTrait {}
impl LocationTrait for Country {}
impl LocationTrait for City {}

// A custom struct on which you can implement the deserialize trait
// Needed as both Deserialize and Box are defined outside this crate.
struct DynLocation(Box<dyn LocationTrait>);

impl<'de> Deserialize<'de> for DynLocation {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // Tricky part ommited here:
        // You will need to partially deserialize you object
        // in order to get a first discriminant before instanciating
        // and deserializing the proper type.
        unimplemented!()
    }
}

// The public method to hide the DynLocation wrapper
pub fn deserialize(item: &str) -> Box<dyn LocationTrait> {
    let location: DynLocation = serde_json::from_str(item).expect("invalid json");
    location.0
}

关于同一主题的一些讨论可以在 How can deserialization of polymorphic trait objects be added in Rust if at all? 中找到.

关于serialization - 如何将 BSON 反序列化为通用对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54044755/

相关文章:

android - 可在哪里/何时使用 describeContents()?

rust - 避免 "use of possibly uninitialized variable"结构中的 "match"在 Rust 中 undefined variable 时无法访问

java - 连接被对等方重置 : socket write error While tring to send object

Python:在 Foo 类中:x = MyClass() MyClass 可以知道它所分配的变量的名称吗?

rust - 从 LLVM 位码生成 Rust 可执行文件

string - 如何在 Rust 中删除文本文件的第一行?

c# - 牛顿软件 JSON : Error getting value from 'NativeCalendarName' on 'System.Globalization.DateTimeFormatInfo'

Java 序列化 - java.io.InvalidClassException 本地类不兼容

c# - 序列化-反序列化(二进制)

c# - C# 中的 Google Protocol Buffer