更复杂的是,serde的枚举反序列化只允许对枚举标记使用字符串、字节或u32
(每种格式都从这三种格式中 Select 一种).这被硬编码到每种格式中,例如here in bincode.就serde而言,具有u64
标记的枚举本质上不是枚举.
因此,考虑到这一点,您必须将枚举序列化和反序列化为枚举以外的其他对象.我 Select 使用元组,它可能是您所能得到的最接近枚举的元组.序列化非常简单,因为我们知道所有东西都是什么类型.
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
作为判别式,然后根据该判别式使用内部数据.
Methods that don't work
您不能通过将虚拟字段添加到枚举来序列化它,因为这仍将使用u32
个标记.您可以try 的另一件事是反序列化(u64, UntaggedEnum)
,其中UntaggedEnum
是:
#[derive(Deserialize)]
#[serde(untagged)]
UntaggedEnum {
A(X),
B(Y),
C(Z),
}
这不起作用,因为非自描述格式不能处理未标记的枚举.最重要的是,如果数据对多个变量有效,即使是自描述格式也可能失败,因为没有简单的方法来根据第一个元素有条件地反序列化元组的第二个元素.它的效率也很低,因为即使u64
无效,它也会try 反序列化枚举.
Notes
您可能在不知道serde枚举需要为u32
的情况下添加了#[repr(u64)]
个枚举,实际上您也可以使用#[repr(u32)]
个枚举.如果是这样的话,似乎可以使用serde的枚举反序列化,而且会稍微简单一些(非常类似于Deserialize
宏生成的内容).据我所知,您需要将这些区别项映射到它们各自的变体.
值得注意的是,我编写的代码从未引用枚举定义中给出的实际判别式.这对于 rust 病来说是非常标准的,因为枚举判别器的功能很少.您甚至需要执行a pointer cast次才能读取它们,并且它们在有条件地反序列化X
、Y
和Z
时完全没有用处.如果移除它们,将不会对序列化产生影响.
如果您打算修改此枚举,或者如果它有大量变体,那么将其转换为宏将是一个很好的利用时间.作为声明性宏,这不会太难,因为您需要的所有值都是像0x01
这样的字面值,而且重复很明显.
我还没有看过bincode2.0,它包含了自己的不使用serde的Decode
特征.可能可以解码u64
个枚举标签,但其 struct 与serde完全不同,所以我没有太深入地研究它.
这可能与您想要的格式不匹配.从u64
epr判断,您试图反序列化的任何内容都不是由serde枚举生成的,所以我不可能知道您的格式是否与我使用的元组格式匹配.