A Short Serde Deserialize example
A Short Serde Deserialize example
In my previous post, I described taking a simple enum and creating a custom type in diesel. This post will take that same enum and implement deserialize.
I often get tripped up by the mechanics of deserializing so this simple enum makes for a good example. Again, this is to benefit anyone looking for more examples of Serde's Deserialize as well as for myself, so I can remember next time I need to do this.
Deserialize in Serde🔗
To refresh, the enum is as follows,
#[derive(Debug,PartialEq,AsExpression,Clone,Serialize)]
#[sql_type = "Bool"]
pub enum PublishState
{
Published,
Unpublished,
Pending,
}
The problem constraint is that PublishState
can be represented as a boolean or a string, so deserialize needs to be able to handle both. The translation from serialized data happens in serde's Visitor trait. The first thing we need is to create a Visitor struct, let's call it PublishStateVisitor
. It doesn't have any members, so it is a Unit Struct. Since this is a fairly simple case, let's just look at all the code at once.
struct PublishStateVisitor
impl<'de> Visitor<'de> for PublishStateVisitor {
type Value = PublishState;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a boolean or string of \"Published\", \"Unpublished\", \"Pending\".")
}
fn visit_bool<E>(self, value: bool) -> Result<PublishState, E>
where
E: de::Error,
{
match value {
true => Ok(PublishState::Published),
false => Ok(PublishState::Unpublished),
}
}
fn visit_str<E>(self, value: &str) -> Result<PublishState, E>
where
E: de::Error,
{
match value {
"Published" => Ok(PublishState::Published),
"Unpublished" | "Pending" => Ok(PublishState::Unpublished),
_s => Err(E::custom(format!("Unknown string value: {}", _s))),
}
}
}
In this case, the Visitor trait will implement the expecting
function and two translation functions, visit_bool
and visit_str
. Since I will accept deserializing from a bool type and a string type, those two translation functions needs to be implemented (see serde's docs for other visitor functions). The visitor translation functions are pretty simple. It's worth noting the return type; they need to return Result<PublishState, E>
where the PublishState
is the result of the translation from bool or str into a PublishState enum value.
Now we need to use the PublishStateVisitor
somewhere; this is where the Deserialize trait comes into play.
impl<'de> Deserialize<'de> for PublishState {
fn deserialize<D>(deserializer: D) -> Result<PublishState, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(PublishStateVisitor)
}
}
Those two trait implementations is all that is needed for this struct. In someways is a lot of code for such a simple translation, but in other ways, it starts to show you the power of serde.
Related and Recommended Reading🔗
Serde's docs are pretty good. I recommend their documentation page as well as their Github Page for JSON examples.
My Example code can be found in my gitlab examples repo.