当必须知道序列长度和类型时,在 Serde 中序列化序列?
Serializing a sequence in Serde when both sequence length and types must be known?
我正在用 Rust 开发一个 Serde 序列化程序,用于 JSON 类二进制格式。
它根据每个元素的大小是否相同,或者是否是混合元素大小的序列,对序列使用不同的二进制编码。
例如:
[1, 2, 3]
:使用紧凑编码进行序列化,因为所有元素都序列化为相同的字节长度
[1, "two", ["a", 1]]
:使用不同的编码进行序列化,因为每个元素序列化为不同的字节长度
根据我对 Serde 的了解,它一次序列化一个元素的序列,并且在序列序列化开始时(通过调用 serialize_seq on a Serializer)只有序列长度是可选的。
是否有一个很好的模式来处理上述情况,在检查所有元素(并序列化以了解它们的字节长度)之前不能序列化序列?它还需要处理嵌套序列,如上例所示。
使用 mcarton 关于在序列序列化程序中存储元素的建议解决了问题。
这会将每个序列化的字节序列存储在 Vec
中(即作为 Vec<Vec<u8>>
),并在序列化为 output
之前检查所有项目的长度是否相等。
这看起来像:
/// Main top level serializer.
pub struct Serializer {
output: Vec<u8>
}
impl<'a> ser::Serializer for &'a mut Serializer {
// ... skipped ...
type SerializeSeq = ArraySerializer<'a>;
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
let array_ser = ArraySerializer {
items: Vec::new(),
output: &mut self.output,
};
Ok(array_ser)
}
// ... skipped ...
}
pub struct ArraySerializer<'a> {
/// Temporary storage for individual serialized array elements.
items: Vec<Vec<u8>>,
/// Storage for final serialized output of header plus all elements. This is
/// typically a reference to the full output buffer being serialized into.
output: &'a mut Vec<u8>,
}
impl <'a> ser::SerializeSeq for ArraySerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<Self::Ok> where
T: ?Sized + Serialize {
// default serializer used for serializing array elements
let mut serializer = Serializer::default();
// serialize individual item and add to `items`
value.serialize(&mut serializer)?;
self.items.push(serializer.output);
Ok(())
}
fn end(self) -> Result<Self::Ok> {
if self.items.is_empty() {
self.output.push(EMPTY_ARRAY_HEADER);
return Ok(());
}
let all_elems_same_length = self.items
.iter()
.all(|ref v| v.len() == self.items[0].len());
};
if all_elems_same_length {
self.output.push(SAME_LENGTH_HEADER);
for item in &mut self.items.iter_mut() {
self.output.append(item);
}
} else {
self.output.push(VARIABLE_LENGTH_HEADER);
// ... skipped: encode rest of items using more complicated serialization ...
}
Ok(())
}
}
我正在用 Rust 开发一个 Serde 序列化程序,用于 JSON 类二进制格式。
它根据每个元素的大小是否相同,或者是否是混合元素大小的序列,对序列使用不同的二进制编码。
例如:
[1, 2, 3]
:使用紧凑编码进行序列化,因为所有元素都序列化为相同的字节长度[1, "two", ["a", 1]]
:使用不同的编码进行序列化,因为每个元素序列化为不同的字节长度
根据我对 Serde 的了解,它一次序列化一个元素的序列,并且在序列序列化开始时(通过调用 serialize_seq on a Serializer)只有序列长度是可选的。
是否有一个很好的模式来处理上述情况,在检查所有元素(并序列化以了解它们的字节长度)之前不能序列化序列?它还需要处理嵌套序列,如上例所示。
使用 mcarton 关于在序列序列化程序中存储元素的建议解决了问题。
这会将每个序列化的字节序列存储在 Vec
中(即作为 Vec<Vec<u8>>
),并在序列化为 output
之前检查所有项目的长度是否相等。
这看起来像:
/// Main top level serializer.
pub struct Serializer {
output: Vec<u8>
}
impl<'a> ser::Serializer for &'a mut Serializer {
// ... skipped ...
type SerializeSeq = ArraySerializer<'a>;
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
let array_ser = ArraySerializer {
items: Vec::new(),
output: &mut self.output,
};
Ok(array_ser)
}
// ... skipped ...
}
pub struct ArraySerializer<'a> {
/// Temporary storage for individual serialized array elements.
items: Vec<Vec<u8>>,
/// Storage for final serialized output of header plus all elements. This is
/// typically a reference to the full output buffer being serialized into.
output: &'a mut Vec<u8>,
}
impl <'a> ser::SerializeSeq for ArraySerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<Self::Ok> where
T: ?Sized + Serialize {
// default serializer used for serializing array elements
let mut serializer = Serializer::default();
// serialize individual item and add to `items`
value.serialize(&mut serializer)?;
self.items.push(serializer.output);
Ok(())
}
fn end(self) -> Result<Self::Ok> {
if self.items.is_empty() {
self.output.push(EMPTY_ARRAY_HEADER);
return Ok(());
}
let all_elems_same_length = self.items
.iter()
.all(|ref v| v.len() == self.items[0].len());
};
if all_elems_same_length {
self.output.push(SAME_LENGTH_HEADER);
for item in &mut self.items.iter_mut() {
self.output.append(item);
}
} else {
self.output.push(VARIABLE_LENGTH_HEADER);
// ... skipped: encode rest of items using more complicated serialization ...
}
Ok(())
}
}