问题描述
我有一个大型的、有点复杂的数据结构,我可以使用 serde
和 rmp-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 changelog 和 rmp_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
}
}