我可以将 RMPV `Value` 发送回 rmp_serde 进行反序列化吗?

问题描述

我有一个大型的、有点复杂的数据结构,我可以使用 serdermp-serde 对其进行序列化和反序列化,但我发现反序列化非常慢。我认为这是因为我的数据结构包括两个相当大的 HashMap。我不知道 rmp_serde::from_slice 创建 HashMap 的效率有多高——它会使用 .with_capacity 进行初始化还是只是创建一个 HashMap 并一一插入?此外,我发现 AHashMap 在其他地方给了我相当大的性能改进,所以我试图避免使用认的 HashMap。

我想尝试使用 rmpv::decode::value::read_value 进行反序列化,但我想将大部分反序列化留给 rmp_serde 并且仅在给定一些 Value 的情况下自己实现一些反序列化。有没有办法选择我手动反序列化的部分?

从概念上讲,我想做的是:

let v = rmp::decode::read_value(&mut reader).unwrap();   // get some Value
let arr : &Vec<Value> = v.as_array().unwrap();           // v is kNown to be an array
let first_value : MyType = deserialize_manually(arr[0]); // I'll convert the Value myself
let second_value : AnotherType = arr[1].into();          // allow rmpv to convert Value for me

我目前使用的是 rmp-serde 0.14 和 rmpv 0.4.7。 The rmp_serde changelogrmp_serde release page 没有提供有关更改内容的详细信息,因此我没有理由相信升级到当前版本(撰写此问题时为 v0.15.4)会提供任何新功能

我知道 serde 提供了一个 deserialize_with attribute。也许这是合适的路线,所以我的问题是:如何使用 deserialize_with 反序列化特定的 MsgPack 字段?

解决方法

我能够使用 deserialize-with 使其工作。首先,我必须注释我的结构:

struct MyStruct {
    some_number: i32,#[serde(deserialize_with="de_first_value")]
    first_value : HashMap<i32,String>,// T1 & T2 don't matter
    second_value : AnotherType,}

然后我创建了这个函数来驱动反序列化。因为我正在反序列化 HashMap,所以我遵循 serde 的 Implement Deserialize for a custom map type

fn de_first_value<'de,D>(deserializer: D) -> Result<HashMap<i32,D::Error>
where
    D: serde::de::Deserializer<'de>,{
    deserializer.deserialize_byte_buf(MyHmVisitor)
}

然后我定义 MyHmVisitor 并实现 the Visitor trait。为了反序列化 HashMap,我必须实现 visit_map 函数;我假设我可以通过实现 the default provided methods 以这种方式对其他类型进行类似的反序列化(除非被覆盖,否则所有类型都会失败):

struct MyHmVisitor;

impl<'de> serde::de::Visitor<'de> for MyHmVisitor {
    type Value = HashMap<i32,String>;

    /// "This is used in error messages. The message should complete the sentence
    /// 'This Visitor expects to receive ...'"
    /// https://docs.serde.rs/src/serde/de/mod.rs.html#1270
    fn expecting(&self,formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter,"a HashMap<i32,String>")
    }

    fn visit_map<A>(self,mut map: A) -> Result<Self::Value,A::Error>
    where
        A: serde::de::MapAccess<'de>,{
        // extract the size hint from the serialized map. If it doesn't exist,default to 0
        let capacity = map.size_hint().unwrap_or(0);

        let mut hm = HashMap::with_capacity(capacity);

        while let Some((k,v)) = map.next_entry()? {
            hm.insert(k,v);
        }

        hm
    }
}