【问题标题】:Serde conditionally deserialize each item in a sequenceSerde 有条件地反序列化序列中的每个项目
【发布时间】:2021-09-04 18:07:47
【问题描述】:

我正在尝试为枚举类型Command 的向量编写反序列化器。我希望它有条件地反序列化;如果无符号字节值小于 100,则它是 Element,并且将像任何其他 Vec<u8> 一样被反序列化。否则,如果无符号字节值介于 100 和 2^8 之间,则它是一个操作,将被反序列化为 u8。我有以下工作实现:

Cargo.toml

[dependencies]
bincode = "1.0"
serde = { version = "1.0.99" }

ma​​in.rs

use serde::de::{Deserialize, Deserializer, Visitor, SeqAccess};
use bincode::{DefaultOptions, Options};
use std::fmt;

#[derive(Debug)]
enum Command {
    Operation(u8),
    Element(Vec<u8>)
}

struct VecVisitor;
impl<'de> Visitor<'de> for VecVisitor {
    type Value = Vec<Command>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("Commands")
    }

    fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error> where V: SeqAccess<'de>,
    {
        let length = seq.size_hint().unwrap();
        let mut commands: Vec<Command> = Vec::new();

        let mut count = 0;
        while count < length {
            count += 1;
            let current: u8 = seq.next_element()?.unwrap();
            match current {
                1..=100 => {
                    let mut v: Vec<u8> = Vec::new();
                    for _ in 0..current {
                        v.push(seq.next_element()?.unwrap());
                    }
                    commands.push(Command::Element(v));
                    count += current as usize;
                }
                _ => {
                    commands.push(Command::Operation(current))
                }
            }
        }

        Ok(commands)
    }
}

#[derive(Debug)]
struct MyVec(Vec<Command>);

impl<'de> Deserialize<'de> for MyVec {
    fn deserialize<D>(deserializer: D) -> Result<MyVec, D::Error> where D: Deserializer<'de>,
    {
        Ok(MyVec(deserializer.deserialize_seq(VecVisitor)?))
    }
}

fn main() {
    let bytes = vec![9u8, 150u8, 175u8, 3u8, 1u8, 2u8, 3u8, 2u8, 3u8, 2u8]; // 150 = Operation, 175 = Operation, 3 = Element, 2 = Element

    let commands: MyVec = DefaultOptions::new()
                    .with_varint_encoding()
                    .deserialize(&bytes).unwrap();

    println!("{:?}", commands);
}

输出:

MyVec([Operation(150), Operation(175), Element([1, 2, 3]), Element([3, 2])])

有没有更简洁的写法?

我已尝试将 Element 解析替换为以下内容,但它仅适用于向量长度恒定的情况(例如 3):

            match current {
                1..=100 => {
                    let v = seq.next_element::<[u8; 3]>()?.unwrap().to_vec();
                    commands.push(Command::Element(v));
                    count += current as usize;
                }

我认为最好的解决方案是通过查看seq (SeqAccess) 的下一个元素,以某种方式有条件地消耗seq 中的下一个元素,如果该元素小于100,它应该像任何其他Vec&lt;u8&gt; 一样反序列化。

【问题讨论】:

    标签: rust serde


    【解决方案1】:

    老实说,我认为这里的循环很好,因为它使您的意图非常清楚。使代码更简洁,同时更惯用的更好方法是去掉 count 并将其替换为 while let 循环,如下所示:

    fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
    where
        V: SeqAccess<'de>,
    {
        let mut commands: Vec<Command> = Vec::new();
        while let Some(current) = seq.next_element()? {
            match current {
                1..=100 => {
                    let mut v = Vec::new();
                    for _ in 0..current {
                        v.push(seq.next_element()?.unwrap());
                    }
                    commands.push(Command::Element(v));
                }
                _ => commands.push(Command::Operation(current)),
            }
        }
        Ok(commands)
    }
    

    如果您真的想摆脱循环,可以使用迭代器并将其收集到向量中(如下所示),但我发现这不太清楚,坦率地说没有必要。

    1..=100 => {
        commands.push(Command::Element(
            (0..current)
                .map(|_| seq.next_element().map(Option::unwrap))
                .collect::<Result<Vec<u8>, _>>()?,
        ));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-23
      • 1970-01-01
      • 2018-08-25
      • 2023-02-22
      • 2018-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多