Implementing Deserialize
The Deserialize trait looks like this:
pub trait Deserialize<'de>: Sized {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>;
}
This method's job is to map the type into the Serde data model by providing
the Deserializer with a Visitor that can be driven by the Deserializer
to construct an instance of your type.
In most cases Serde's derive is able to generate an appropriate implementation
of Deserialize for structs and enums defined in your crate. Should you need to
customize the deserialization behavior for a type in a way that derive does not
support, you can implement Deserialize yourself. Implementing Deserialize
for a type tends to be more complicated than implementing Serialize.
The Deserializer trait supports two entry point styles which enables different
kinds of deserialization.
The
deserialize_anymethod. Self-describing data formats like JSON are able to look at the serialized data and tell what it represents. For example the JSON deserializer may see an opening curly brace ({) and know that it is seeing a map. If the data format supportsDeserializer::deserialize_any, it will drive the Visitor using whatever type it sees in the input. JSON uses this approach when deserializingserde_json::Valuewhich is an enum that can represent any JSON document. Without knowing what is in a JSON document, we can deserialize it toserde_json::Valueby going throughDeserializer::deserialize_any.The various other
deserialize_*methods. Non-self-describing formats like Postcard need to be told what is in the input in order to deserialize it. Thedeserialize_*methods are hints to the deserializer for how to interpret the next piece of input. Non-self-describing formats are not able to deserialize something likeserde_json::Valuewhich relies onDeserializer::deserialize_any.
When implementing Deserialize, you should avoid relying on
Deserializer::deserialize_any unless you need to be told by the Deserializer
what type is in the input. Know that relying on Deserializer::deserialize_any
means your data type will be able to deserialize from self-describing formats
only, ruling out Postcard and many others.
The Visitor trait
A Visitor is instantiated by a Deserialize impl and passed to a
Deserializer. The Deserializer then calls a method on the Visitor in order
to construct the desired type.
Here is a Visitor that is able to deserialize a primitive i32 from a variety
of types.
use std::fmt;
use serde::de::{self, Visitor};
struct I32Visitor;
impl<'de> Visitor<'de> for I32Visitor {
type Value = i32;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an integer between -2^31 and 2^31")
}
fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(i32::from(value))
}
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(value)
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
use std::i32;
if value >= i64::from(i32::MIN) && value <= i64::from(i32::MAX) {
Ok(value as i32)
} else {
Err(E::custom(format!("i32 out of range: {}", value)))
}
}
// Similar for other methods:
// - visit_i16
// - visit_u8
// - visit_u16
// - visit_u32
// - visit_u64
}
The Visitor trait has lots more methods that are not implemented for
I32Visitor. Leaving them unimplemented means a type error is returned if
they get called. For example I32Visitor does not implement
Visitor::visit_map, so trying to deserialize an i32 when the input contains a
map is a type error.
Driving a Visitor
Deserialize a value by passing a Visitor to the given Deserializer. The
Deserializer will call one of the Visitor methods depending on the input
data, which is known as "driving" the Visitor.
impl<'de> Deserialize<'de> for i32 {
fn deserialize<D>(deserializer: D) -> Result<i32, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_i32(I32Visitor)
}
}
Note that a Deserializer will not necessarily follow the type hint, so the
call to deserialize_i32 does not necessarily mean the Deserializer will call
I32Visitor::visit_i32. For example JSON treats all signed integer types alike.
The JSON Deserializer will call visit_i64 for any signed integer and
visit_u64 for any unsigned integer, even if hinted a different type.