This crate provides custom de/serialization helpers to use in combination with serde’s with
annotation and with the improved serde_as
-annotation.
Some common use cases are:
Display
and FromStr
traits, e.g., for u8
, url::Url
, or mime::Mime
.
Check DisplayFromStr
for details.serde_as
large arrays are supported, even if they are nested in other types.
[bool; 64]
, Option<[u8; M]>
, and Box<[[u8; 64]; N]>
are all supported, as this examples shows.Option
types with #[skip_serializing_none]
.with_prefix!
.#hash,#tags,#are,#great
into a Vec<String>
.
Check the documentation for serde_with::StringWithSeparator::<CommaSeparator, T>
.Check out the user guide to find out more tips and tricks about this crate.
For further help using this crate you can open a new discussion or ask on users.rust-lang.org. For bugs, please open a new issue on GitHub.
serde_with
in your Project# Add the current version to your Cargo.toml
cargo add serde_with
The crate contains different features for integration with other common crates. Check the feature flags section for information about all available features.
Annotate your struct or enum to enable the custom de/serializer.
The #[serde_as]
attribute must be placed before the #[derive]
.
The as
is analogous to the with
attribute of serde.
You mirror the type structure of the field you want to de/serialize.
You can specify converters for the inner types of a field, e.g., Vec<DisplayFromStr>
.
The default de/serialization behavior can be restored by using _
as a placeholder, e.g., BTreeMap<_, DisplayFromStr>
.
DisplayFromStr
#[serde_as]
#[derive(Deserialize, Serialize)]
struct Foo {
// Serialize with Display, deserialize with FromStr
#[serde_as(as = "DisplayFromStr")]
bar: u8,
}
// This will serialize
Foo {bar: 12}
// into this JSON
{"bar": "12"}
serde does not support arrays with more than 32 elements or using const-generics.
The serde_as
attribute allows circumventing this restriction, even for nested types and nested arrays.
On top of it, [u8; N]
(aka, bytes) can use the specialized "Bytes"
for efficiency much like the serde_bytes
crate.
#[serde_as]
#[derive(Deserialize, Serialize)]
struct Arrays<const N: usize, const M: usize> {
#[serde_as(as = "[_; N]")]
constgeneric: [bool; N],
#[serde_as(as = "Box<[[_; 64]; N]>")]
nested: Box<[[u8; 64]; N]>,
#[serde_as(as = "Option<[_; M]>")]
optional: Option<[u8; M]>,
#[serde_as(as = "Bytes")]
bytes: [u8; M],
}
// This allows us to serialize a struct like this
let arrays: Arrays<100, 128> = Arrays {
constgeneric: [true; 100],
nested: Box::new([[111; 64]; 100]),
optional: Some([222; 128]),
bytes: [0x42; 128],
};
assert!(serde_json::to_string(&arrays).is_ok());
skip_serializing_none
This situation often occurs with JSON, but other formats also support optional fields.
If many fields are optional, putting the annotations on the structs can become tedious.
The #[skip_serializing_none]
attribute must be placed before the #[derive]
.
#[skip_serializing_none]
#[derive(Deserialize, Serialize)]
struct Foo {
a: Option<usize>,
b: Option<usize>,
c: Option<usize>,
d: Option<usize>,
e: Option<usize>,
f: Option<usize>,
g: Option<usize>,
}
// This will serialize
Foo {a: None, b: None, c: None, d: Some(4), e: None, f: None, g: Some(7)}
// into this JSON
{"d": 4, "g": 7}
serde_as
usageThis example is mainly supposed to highlight the flexibility of the serde_as
annotation compared to serde’s with
annotation.
More details about serde_as
can be found in the user guide.
use std::time::Duration;
#[serde_as]
#[derive(Deserialize, Serialize)]
enum Foo {
Durations(
// Serialize them into a list of number as seconds
#[serde_as(as = "Vec<DurationSeconds>")]
Vec<Duration>,
),
Bytes {
// We can treat a Vec like a map with duplicates.
// JSON only allows string keys, so convert i32 to strings
// The bytes will be hex encoded
#[serde_as(as = "Map<DisplayFromStr, Hex>")]
bytes: Vec<(i32, Vec<u8>)>,
}
}
// This will serialize
Foo::Durations(
vec![Duration::new(5, 0), Duration::new(3600, 0), Duration::new(0, 0)]
)
// into this JSON
{
"Durations": [5, 3600, 0]
}
// and serializes
Foo::Bytes {
bytes: vec![
(1, vec![0, 1, 2]),
(-100, vec![100, 200, 255]),
(1, vec![0, 111, 222]),
],
}
// into this JSON
{
"Bytes": {
"bytes": {
"1": "000102",
"-100": "64c8ff",
"1": "006fde"
}
}
}
DeserializeAs
implementationsSerializeAs
implementationsserde_as
to the serde traits.Cow
data during deserialization when possible.Default
on errorDefault
from null
valuesDurationSeconds
with micro-seconds as base unit.DurationSecondsWithFrac
with micro-seconds as base unit.DurationSeconds
with milli-seconds as base unit.DurationSecondsWithFrac
with milli-seconds as base unit.DurationSeconds
with nano-seconds as base unit.DurationSecondsWithFrac
with nano-seconds as base unit.De/Serializer::is_human_readable
, otherwise use the secondVec
elements into key-value map entriesOption<String>
type while transforming the empty string to None
serde_as
to the serde traits.TimestampSeconds
with micro-seconds as base unit.TimestampSecondsWithFrac
with micro-seconds as base unit.TimestampSeconds
with milli-seconds as base unit.TimestampSecondsWithFrac
with milli-seconds as base unit.TimestampSeconds
with nano-seconds as base unit.TimestampSecondsWithFrac
with nano-seconds as base unit.Vec<T>
, skipping elements which fail to deserialize.Deserialize
.Serialize
.serde_as
system.skip_serializing_if
annotations to [Option
] fields.