I'm using [prost] to generate structs from protobuf. One of those structs is quite simple:
enum Direction {
up = 0;
down = 1;
sideways = 2;
}
This generates code which looks like:
#[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,
}
There's a significant number of JSON files I have to parse into these messages. Those are tens of thousands of lines long, but when this field appears it looks like:
{ "direction": "up" }
So, in short, its deserialized format is a string, serialized is i32
.
If I just run that, and try to parse JSON, I get:
thread 'tests::parse_json' panicked at 'Failed to parse: "data/my_data.json": Error("invalid type: string \"up\", expected i32", line: 132, column: 23)
That, of course, makes sense - there's no reflection to guide deserialization from "up"
to 0
.
Question: How can I set up serde
to parse those strings into their matching integer values? I've read the serde docs thoroughly, and it appears I might need to write a custom deserializer for this, although that seems like overkill.
I've tried a few different serde attributes, like:
#[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,
}
with this function:
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),
}
}
}
but, despite what the serde docs tell me, that method doesn't even get called (but compilation succeeds).
I also tried a field attribute on the field which is a Direction
:
#[serde(deserialize_with = \"super::super::common::Direction::from\")]
but that of course wants a different signature than just &str
: the trait std::convert::From<__D> is not implemented for common::Direction
Do I just have to write a custom deserializer? Seems like a common enough use case that there would be a pattern to use.
Note: this is the opposite problem of that solved by serde_repr
. I didn't see a way to put it to work here.
I implemented my own deserializer, thanks to the guide in this answer. There's likely a simpler or more idiomatic approach out there, so if you know one, please share!
Serde attribute, set on the field rather than the Enum:
Deserializer: