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.