如何使用serde反序列化Prost枚举?

8

我正在使用 [prost] 从 protobuf 生成结构体。其中一个结构体非常简单:

enum Direction {
  up = 0;
  down = 1;
  sideways = 2;
}

这将生成类似以下代码的内容:
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
#[derive(serde_derive::Deserialize)]
pub enum Direction {
    Up = 0,
    Down = 1,
    Sideways = 2,
}

我需要解析大量的JSON文件并将其转换为这些消息。这些文件有数万行,但当该字段出现时,它看起来像:

{ "direction": "up" }

简而言之,它的反序列化格式是字符串,序列化格式是i32

如果我仅运行该操作并尝试解析JSON,则会收到以下错误:

thread 'tests::parse_json' panicked at 'Failed to parse: "data/my_data.json": Error("invalid type: string \"up\", expected i32", line: 132, column: 23)

当然,这是有道理的——没有反射来指引将序列化对象从"up"转换为0

问题:我该如何设置serde以将这些字符串解析为相应的整数值?我已经仔细阅读了serde文档,但似乎我需要编写一个自定义的反序列化程序,但那似乎过于复杂了。

我尝试了一些不同的serde属性,例如:

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
#[derive(serde_derive::Deserialize)]
#[serde(from = "&str")] // This line
pub enum Direction {
    Up = 0,
    Down = 1,
    Sideways = 2,
}

使用此函数:

impl From<&str> for Direction {
    fn from(item: &str) -> Self {
        match item {
            "up" => Self::Up,
            "down" => Self::Down,
            "sideways" => Self::Sideways,
            _ => panic!("Invalid value for Direction: {}", item),
        }
    }
}

但是,尽管Serde文档告诉我,那个方法甚至没有被调用(但编译成功)。

我还尝试了在字段上使用一个Direction的字段属性:

#[serde(deserialize_with = \"super::super::common::Direction::from\")]

当然,这需要一个不同于&str的签名: trait std::convert::From<__D>未为common::Direction实现

我是否只需编写自定义反序列化器?看起来这是很常见的用例,应该有一种可用的模式。

注意:这与serde_repr解决的问题相反。我没有看到在这里使用它的方法。


面对相同的问题,20个月后...你是否找到了除下面答案之外的好解决方案? - mjalajel
1个回答

5

我根据这个答案中的指南,实现了自己的反序列化器。可能有更简单或更惯用的方法,如果您知道,请分享!

Serde属性设置在字段上而不是枚举上:

config.field_attribute(
    "direction",
    "#[serde(deserialize_with = \"super::super::common::Direction::from_str\")]"
);

反序列化程序:

impl Direction {
    pub fn from_str<'de, D>(deserializer: D) -> Result<i32, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s: &str = Deserialize::deserialize(deserializer)?;

        match s.to_lowercase().as_str() {
            "up" => Ok(Self::Tx as i32),
            "down" => Ok(Self::Down as i32),
            "sideways" => Ok(Self::Sideways as i32),
            _ => Err(de::Error::unknown_variant(s, &["up", "down", "sideways"])),
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接