2

我正在使用 [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"to 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\")]

但这当然需要一个不同的签名,而不仅仅是&strthe trait std::convert::From<__D> is not implemented for common::Direction

我只需要编写一个自定义反序列化器吗?似乎是一个足够常见的用例,可以使用一种模式。

注意:这是解决的相反问题serde_repr。我没有看到让它在这里工作的方法。

4

1 回答 1

2

感谢本答案中的指南,我实现了自己的反序列化器。那里可能有一种更简单或更惯用的方法,所以如果你知道,请分享!

Serde 属性,设置在字段而不是 Enum 上:

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"])),
        }
    }
}
于 2020-10-19T04:00:27.427 回答