Implementing Serialize
The Serialize
trait looks like this:
pub trait Serialize {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer;
}
This method's job is to take your type (&self
) and map it into the Serde data
model by invoking exactly one of the methods on the given Serializer
.
In most cases Serde's derive is able to generate an appropriate implementation
of Serialize
for structs and enums defined in your crate. Should you need to
customize the serialization behavior for a type in a way that derive does not
support, you can implement Serialize
yourself.
Serializing a primitive
As the simplest example, here is the builtin Serialize
impl for the primitive
i32
.
impl Serialize for i32 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_i32(*self)
}
}
Serde provides such impls for all of Rust's primitive types so you are not
responsible for implementing them yourself, but serialize_i32
and similar
methods may be useful if you have a type that needs to be represented as a
primitive in its serialized form. For example you could serialize a C-like enum
as a primitive number.
Serializing a sequence or map
Compound types follow a three-step process of init, elements, end.
use serde::ser::{Serialize, Serializer, SerializeSeq, SerializeMap};
impl<T> Serialize for Vec<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for e in self {
seq.serialize_element(e)?;
}
seq.end()
}
}
impl<K, V> Serialize for MyMap<K, V>
where
K: Serialize,
V: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.len()))?;
for (k, v) in self {
map.serialize_entry(k, v)?;
}
map.end()
}
}
Serializing a tuple
The serialize_tuple
method is a lot like serialize_seq
. The distinction
Serde makes is that serialize_tuple
is for sequences where the length does not
need to be serialized because it will be known at deserialization time. The
usual examples are Rust tuples and arrays. In non-self-describing formats a
Vec<T>
needs to be serialized with its length in order to be able to
deserialize a Vec<T>
back out. But a [T; 16]
can be serialized using
serialize_tuple
because the length will be known at deserialization time
without looking at the serialized bytes.
Serializing a struct
Serde distinguishes between four types of structs. Ordinary structs and tuple structs follow the three-step process of init, elements, end just like a sequence or map. Newtype structs and unit structs are more like primitives.
// An ordinary struct. Use three-step process:
// 1. serialize_struct
// 2. serialize_field
// 3. end
struct Color {
r: u8,
g: u8,
b: u8,
}
// A tuple struct. Use three-step process:
// 1. serialize_tuple_struct
// 2. serialize_field
// 3. end
struct Point2D(f64, f64);
// A newtype struct. Use serialize_newtype_struct.
struct Inches(u64);
// A unit struct. Use serialize_unit_struct.
struct Instance;
Structs and maps may look similar in some formats, including JSON. The distinction Serde makes is that structs have keys that are compile-time constant strings and will be known at deserialization time without looking at the serialized data. This condition enables some data formats to handle structs much more efficiently and compactly than maps.
Data formats are encouraged to treat newtype structs as insignificant wrappers around the inner value, serializing just the inner value. See for example JSON's treatment of newtype structs.
use serde::ser::{Serialize, Serializer, SerializeStruct};
struct Color {
r: u8,
g: u8,
b: u8,
}
impl Serialize for Color {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// 3 is the number of fields in the struct.
let mut state = serializer.serialize_struct("Color", 3)?;
state.serialize_field("r", &self.r)?;
state.serialize_field("g", &self.g)?;
state.serialize_field("b", &self.b)?;
state.end()
}
}
Serializing an enum
Serializing enum variants is very similar to serializing structs.
enum E {
// Use three-step process:
// 1. serialize_struct_variant
// 2. serialize_field
// 3. end
Color { r: u8, g: u8, b: u8 },
// Use three-step process:
// 1. serialize_tuple_variant
// 2. serialize_field
// 3. end
Point2D(f64, f64),
// Use serialize_newtype_variant.
Inches(u64),
// Use serialize_unit_variant.
Instance,
}
Other special cases
There are two more special cases that are part of the Serializer trait.
There is a method serialize_bytes
which serializes a &[u8]
. Some formats
treat bytes like any other seq, but some formats are able to serialize bytes
more compactly. Currently Serde does not use serialize_bytes
in the
Serialize
impl for &[u8]
or Vec<u8>
but once specialization lands in
stable Rust we will begin using it. For now the serde_bytes
crate can be
used to enable efficient handling of &[u8]
and Vec<u8>
through
serialize_bytes
.
Finally, serialize_some
and serialize_none
correspond to Option::Some
and
Option::None
. Users tend to have different expectations around the Option
enum compared to other enums. Serde JSON will serialize Option::None
as null
and Option::Some
as just the contained value.