#[serde_as]
Expand description
Convenience macro to use the serde_as
system.
The serde_as
system is designed as a more flexible alternative to serde’s with
annotation.
The #[serde_as]
attribute must be placed before the #[derive]
attribute.
Each field of a struct or enum can be annotated with #[serde_as(...)]
to specify which
transformations should be applied. serde_as
is not supported on enum variants.
This is in contrast to #[serde(with = "...")]
.
§Example
use serde_with::{serde_as, DisplayFromStr, Map};
#[serde_as]
#[derive(Serialize, Deserialize)]
struct Data {
/// Serialize into number
#[serde_as(as = "_")]
a: u32,
/// Serialize into String
#[serde_as(as = "DisplayFromStr")]
b: u32,
/// Serialize into a map from String to String
#[serde_as(as = "Map<DisplayFromStr, _>")]
c: Vec<(u32, String)>,
}
§Alternative path to serde_with
crate
If serde_with
is not available at the default path, its path should be specified with the
crate
argument. See re-exporting serde_as
for more use case information.
#[serde_as(crate = "::some_other_lib::serde_with")]
#[derive(Deserialize)]
struct Data {
#[serde_as(as = "_")]
a: u32,
}
§What this macro does
The serde_as
macro only serves a convenience function.
All the steps it performs, can easily be done manually, in case the cost of an attribute macro
is deemed too high. The functionality can best be described with an example.
#[serde_as]
#[derive(serde::Serialize)]
struct Foo {
#[serde_as(as = "Vec<_>")]
bar: Vec<u32>,
#[serde_as(as = "Option<DisplayFromStr>")]
baz: Option<u32>,
}
-
All the placeholder type
_
will be replaced with::serde_with::Same
. The placeholder type_
marks all the places where the type’sSerialize
implementation should be used. In the example, it means that theu32
values will serialize with theSerialize
implementation ofu32
. TheSame
type implementsSerializeAs
whenever the underlying type implementsSerialize
and is used to make the two traits compatible.If you specify a custom path for
serde_with
via thecrate
attribute, the path to theSame
type will be altered accordingly. -
Wrap the type from the annotation inside a
::serde_with::As
. In the above example we now have something like::serde_with::As::<Vec<::serde_with::Same>>
. TheAs
type acts as the opposite of theSame
type. It allows using aSerializeAs
type whenever aSerialize
is required. -
Translate the
*as
attributes into the serde equivalent ones.#[serde_as(as = ...)]
will become#[serde(with = ...)]
. Similarly,serialize_as
is translated toserialize_with
.The field attributes will be kept on the struct/enum such that other macros can use them too.
-
It searches
#[serde_as(as = ...)]
if there is a type namedBorrowCow
under any path. IfBorrowCow
is found, the attribute#[serde(borrow)]
is added to the field. If#[serde(borrow)]
or#[serde(borrow = "...")]
is already present, this step will be skipped. -
Restore the ability of accepting missing fields if both the field and the transformation are
Option
.An
Option
is detected by an exact text match. Renaming an import or type aliases can cause confusion here. The following variants are supported.Option
std::option::Option
, with or without leading::
core::option::Option
, with or without leading::
If the field is of type
Option<T>
and the attribute#[serde_as(as = "Option<S>")]
(alsodeserialize_as
; for anyT
/S
) then#[serde(default)]
is applied to the field.This restores the ability of accepting missing fields, which otherwise often leads to confusing serde_with#185.
#[serde(default)]
is not applied, if it already exists. It only triggers if both field and transformation areOption
s. For example, using#[serde_as(as = "NoneAsEmptyString")]
onOption<String>
will not see any change.If the automatically applied attribute is undesired, the behavior can be suppressed by adding
#[serde_as(no_default)]
.This can be combined like
#[serde_as(as = "Option<S>", no_default)]
.
After all these steps, the code snippet will have transformed into roughly this.
#[derive(serde::Serialize)]
struct Foo {
#[serde_as(as = "Vec<_>")]
#[serde(with = "::serde_with::As::<Vec<::serde_with::Same>>")]
bar: Vec<u32>,
#[serde_as(as = "Option<DisplayFromStr>")]
#[serde(default)]
#[serde(with = "::serde_with::As::<Option<DisplayFromStr>>")]
baz: Option<u32>,
}
§A note on schemars
integration
When the schemars_0_8
feature is enabled this macro will scan for
#[derive(JsonSchema)]
attributes and, if found, will add
#[schemars(with = "Schema<T, ...>")]
annotations to any fields with a
#[serde_as(as = ...)]
annotation. If you wish to override the default
behavior here you can add #[serde_as(schemars = true)]
or
#[serde_as(schemars = false)]
.
Note that this macro will check for any of the following derive paths:
JsonSchema
schemars::JsonSchema
::schemars::JsonSchema
It will also work if the relevant derive is behind a #[cfg_attr]
attribute
and propagate the #[cfg_attr]
to the various #[schemars]
field attributes.