让事情变得复杂的是 serde 的枚举反序列化只允许字符串、字节或u32
对于枚举标记(每种格式选择这三种格式之一)。例如,这被硬编码到每种格式中这里是二进制代码 https://docs.rs/bincode/latest/src/bincode/de/mod.rs.html#284。带有一个枚举u64
就 serde 而言,tag 本质上不是枚举。
因此,考虑到这一点,您必须将枚举序列化和反序列化为枚举以外的其他内容。我选择使用元组,它可能与您所得到的最接近的枚举。序列化非常简单,因为我们知道所有内容是什么类型。
impl Serialize for StreamOutputFormat {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
StreamOutputFormat::A(x) => {
let mut tuple = serializer.serialize_tuple(2)?;
tuple.serialize_element(&0x01u64)?;
tuple.serialize_element(x)?;
tuple.end()
}
StreamOutputFormat::B(y) => {
let mut tuple = serializer.serialize_tuple(2)?;
tuple.serialize_element(&0x02u64)?;
tuple.serialize_element(y)?;
tuple.end()
}
StreamOutputFormat::C(z) => {
let mut tuple = serializer.serialize_tuple(2)?;
tuple.serialize_element(&0x03u64)?;
tuple.serialize_element(z)?;
tuple.end()
}
}
}
}
为了清晰起见,我一直重复这一点。如果您的变体具有多个字段,则需要增加传递给的数字serialize_tuple
。也不要忘记判别式需要u64
.
现在进行反序列化。
impl<'de> Deserialize<'de> for StreamOutputFormat {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
struct StreamOutputFormatVisitor;
impl<'de> Visitor<'de> for StreamOutputFormatVisitor {
type Value = StreamOutputFormat;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
formatter,
"a tuple of size 2 consisting of a u64 discriminant and a value"
)
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let discriminant: u64 = seq
.next_element()?
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
match discriminant {
0x01 => {
let x = seq
.next_element()?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
Ok(StreamOutputFormat::A(x))
}
0x02 => {
let y = seq
.next_element()?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
Ok(StreamOutputFormat::B(y))
}
0x03 => {
let z = seq
.next_element()?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
Ok(StreamOutputFormat::C(z))
}
d => Err(A::Error::invalid_value(
serde::de::Unexpected::Unsigned(d),
&"0x01, 0x02, or 0x03",
)),
}
}
}
deserializer.deserialize_tuple(2, StreamOutputFormatVisitor)
}
}
通过样板,我们有一个电话deserialize_tuple
,这将调用visit_seq
在访客身上。在该方法中,访问者消耗了u64
用作判别式,然后根据该判别式使用内部数据。
不起作用的方法
您无法通过向枚举添加虚拟字段来序列化它,因为这仍然会使用u32
标签。您可以尝试的另一件事是通过反序列化(u64, UntaggedEnum)
where UntaggedEnum
is:
#[derive(Deserialize)]
#[serde(untagged)]
UntaggedEnum {
A(X),
B(Y),
C(Z),
}
这不起作用,因为非自描述格式无法处理未标记的枚举。最重要的是,如果数据对多个变体有效,即使是自描述格式也可能会失败,因为没有简单的方法可以根据第一个元素有条件地反序列化元组的第二个元素。它的效率也很低,因为它会尝试反序列化枚举,即使u64
无效。
Notes
您可能已添加#[repr(u64)]
不知道 serde 枚举需要u32
,你实际上可以使用#[repr(u32)]
枚举也是如此。如果是这样的话,看来你可以使用 serde 的 enum 反序列化,它会稍微简单一些(与Deserialize
宏生成)。据我所知,您需要将判别式映射到它们各自的变体。
值得注意的是,我编写的代码从未引用枚举定义中给出的实际判别式。这对于 Rust 来说是相当标准的,因为枚举判别式的功能非常少。你需要做指针转换 https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting甚至阅读它们,它们在有条件反序列化中绝对没有用处X
, Y
, and Z
。如果删除它们,不会对序列化产生影响。
如果您打算修改此枚举,或者它有大量变体,那么最好利用时间将其转换为宏。作为声明性宏,这不会太难,因为您需要的所有值都是像这样的文字0x01
并且重复是显而易见的。
我还没有检查过 bincode 2.0,其中包括它自己的Decode
不使用 serde 的特征。或许可以解码u64
enum标签,但结构与serde完全不同,所以我没有过多研究它。
这可能与您想要的格式不匹配。判断通过u64
repr,您尝试反序列化的任何内容都不是由 serde 枚举生成的,因此我不可能知道您的格式是否与我使用的元组格式匹配。