[英]Serde MsgPack versioning
I need to serialize some data into files.我需要将一些数据序列化为文件。 For the sake of memory efficiency, I want to use the default compact serializer of MessagePack (MsgPack), as it only serializes field values w/o their names.
为了 memory 效率,我想使用 MessagePack (MsgPack) 的默认紧凑序列化程序,因为它只序列化不带名称的字段值。 I also want to be able to make changes to the data structure in future versions, which obviously can't be done w/o also storing some meta/versioning information.
我还希望能够在未来版本中对数据结构进行更改,这显然无法在不存储一些元/版本信息的情况下完成。 I imagine the most efficient way to do it is to simply use some "header" field for that purpose.
我想最有效的方法是为此目的简单地使用一些“标题”字段。 Here is an example:
这是一个例子:
pub struct Data {
pub version: u8,
pub items: Vec<Item>,
}
pub struct Item {
pub field_a: i32,
pub field_b: String,
pub field_c: i16, // Added in version 3
}
Can I do something like that in rmp-serde (or maybe some other crate?) - to somehow annotate that a certain struct field should only be taken into account for specific file versions?我可以在rmp-serde (或者其他一些 crate 吗?)中做类似的事情 - 以某种方式注释某个结构字段应该只考虑特定文件版本?
You can achieve this by writing a custom deserializer like this:您可以通过编写这样的自定义反序列化器来实现这一点:
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Serialize)]
pub struct Data {
pub version: u8,
pub items: Vec<Item>,
}
#[derive(Serialize)]
pub struct Item {
pub field_a: i32,
pub field_b: String,
pub field_c: i16, // Added in version 3
}
impl<'de> Deserialize<'de> for Data {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// Inner structs, used for deserializing only
#[derive(Deserialize)]
pub struct InnerData {
version: u8,
items: Vec<InnerItem>,
}
#[derive(Deserialize)]
pub struct InnerItem {
field_a: i32,
field_b: String,
field_c: Option<i16>, // Added in version 3 - note that this field is optional
}
// Deserializer the inner structs
let inner_data = InnerData::deserialize(deserializer)?;
// Get the version so we can add custom logic based on the version later on
let version = inner_data.version;
// Map the InnerData/InnerItem structs to Data/Item using our version based logic
Ok(Data {
version,
items: inner_data
.items
.into_iter()
.map(|item| {
Ok(Item {
field_a: item.field_a,
field_b: item.field_b,
field_c: if version < 3 {
42 // Default value
} else {
// Get the value of field_c
// If it's missing return an error, since it's required since version 3
// Otherwise return the value
item.field_c
.map_or(Err(D::Error::missing_field("field_c")), Ok)?
},
})
})
.collect::<Result<_, _>>()?,
})
}
}
Short explanation how the deserializer works:解串器如何工作的简短说明:
D::Error::missing_field
errorD::Error::missing_field
错误
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.