Attribute Macro serde_with::apply

source ·
#[apply]
Available on crate feature macros only.
Expand description

Apply attributes to all fields with matching types

Whenever you experience the need to apply the same attributes to multiple fields, you can use this macro. It allows you to specify a list of types and a list of attributes. Each field with a “matching” type will then get the attributes applied. The apply attribute must be placed before any consuming attributes, such as derive or serde_as, because Rust expands all attributes in order.

For example, if your struct or enum contains many Option<T> fields, but you do not want to serialize None values, you can use this macro to apply the #[serde(skip_serializing_if = "Option::is_none")] attribute to all fields of type Option<T>.

#[serde_with::apply(
    Option => #[serde(skip_serializing_if = "Option::is_none")],
)]
#[derive(serde::Serialize)]
struct Data {
    a: Option<String>,
    b: Option<u64>,
    c: Option<String>,
    d: Option<bool>,
}

Each rule starts with a type pattern, specifying which fields to match and a list of attributes to apply. Multiple rules can be provided in a single apply attribute.

#[serde_with::apply(
    Option => #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")],
    Option<bool> => #[serde(rename = "bool")],
)]

§Type Patterns

The type pattern left of the => specifies which fields to match.

Type PatternMatching TypesNotes
_Option<bool>
BTreeMap<&'static str, Vec<u32>>
_ matches all fields.
OptionOption<bool>
Option<String>
A missing generic is compatible with any generic arguments.
Option<bool>Option<bool>A fully specified type only matches exactly.
BTreeMap<String, u32>BTreeMap<String, u32>A fully specified type only matches exactly.
BTreeMap<String, _>BTreeMap<String, u32>
BTreeMap<String, bool>
Any String key BTreeMap matches, as the value is using the _ placeholder.
[u8; _][u8; 1]
[u8; N]
_ also works as a placeholder for any array length.

§Opt-out for Individual Fields

The apply attribute will find all fields with a compatible type. This can be overly eager and a different set of attributes might be required for a specific field. You can opt-out of the apply attribute by adding the #[serde_with(skip_apply)] attribute to the field. This will prevent any apply to apply to this field. If two rules apply to the same field, it is impossible to opt-out of only a single one. In this case the attributes must be applied to the field manually.

#[serde_with::apply(
    Option => #[serde(skip_serializing_if = "Option::is_none")],
)]
#[derive(serde::Serialize)]
struct Data {
    a: Option<String>,
    #[serde_with(skip_apply)]
    always_serialize_this_field: Option<u64>,
    c: Option<String>,
    d: Option<bool>,
}

let data = Data {
    a: None,
    always_serialize_this_field: None,
    c: None,
    d: None,
};

// serializes into this JSON:
{
    "always_serialize_this_field": null
}

§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_with::apply(
    crate = "::some_other_lib::serde_with"
    Option => #[serde(skip_serializing_if = "Option::is_none")],
)]
#[derive(serde::Serialize)]
struct Data {
    a: Option<String>,
    b: Option<u64>,
    c: Option<String>,
    d: Option<bool>,
}