[][src]Module serde_with::guide::serde_as

serde_as Annotation

This is an alternative to serde's with-annotation. It is more flexable and composable but work with fewer types.

The scheme is based on two new traits, SerializeAs and DeserializeAs, which need to be implemented by all types which want to be compatible with serde_as. The proc macro attribute #[serde_as] exists as a usability boost for users.

This site contains some general advice how to use this crate and then lists the implemented conversions for serde_as. The basic design of the system was done by @markazmierczak.

  1. Switching from serde's with to serde_as
    1. Implementing SerializeAs / DeserializeAs
  2. De/Serialize Implementations Available
    1. Bytes / Vec<u8> to hex string
    2. De/Serialize with FromStr and Display
    3. Duration as seconds
    4. Ignore deserialization errors
    5. Maps to Vec of tuples
    6. NaiveDateTime like UTC timestamp
    7. None as empty String
    8. Timestamps as seconds since UNIX epoch
    9. Value into JSON String
    10. Vec of tuples to Maps

Switching from serde's with to serde_as

For the user the main difference is that instead of

This example is not tested
#[serde(with = "...")]

you now have to write

This example is not tested
#[serde_as(as = "...")]

and place the #[serde_as] attribute before the #[derive] attribute. You still need the #[derive(Serialize, Deserialize)] on the struct/enum.

All together this looks like:

use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};

#[serde_as]
#[derive(Serialize, Deserialize)]
struct A {
    #[serde_as(as = "DisplayFromStr")]
    mime: mime::Mime,
}

The main advantage is that you can compose serde_as stuff, which is not possible with the with-annotation. For example, the mime field from above could be nested in one or more data structures:

#[serde_as]
#[derive(Serialize, Deserialize)]
struct A {
    #[serde_as(as = "Option<BTreeMap<_, Vec<DisplayFromStr>>>")]
    mime: Option<BTreeMap<String, Vec<mime::Mime>>>,
}

Implementing SerializeAs / DeserializeAs

You can support SerializeAs / DeserializeAs on your own types too. Most "leaf" types do not need to implement these traits since they are supported implicitly. "Leaf" type refers to types which directly serialize like plain data types. SerializeAs / DeserializeAs is very important for collection types, like Vec or BTreeMap, since they need special handling for they key/value de/serialization such that the conversions can be done on the key/values. You also find them implemented on the conversion types, such as the DisplayFromStr type. These make up the bulk of this crate and allow you to perform all the nice conversions to hex strings, the bytes to string converter, or duration to UNIX epoch.

De/Serialize Implementations Available

Bytes / Vec<u8> to hex string

Hex

Requires the hex feature.

This example is not tested
// Rust
#[serde_as(as = "serde_with::hex::Hex")]
value: Vec<u8>,

// JSON
"value": "deadbeef",

De/Serialize with FromStr and Display

Useful if a type implements FromStr / Display but not Deserialize / Serialize.

DisplayFromStr

This example is not tested
// Rust
#[serde_as(as = "serde_with::DisplayFromStr")]
value: u128,
#[serde_as(as = "serde_with::DisplayFromStr")]
mime: mime::Mime,

// JSON
"value": "340282366920938463463374607431768211455",
"mime": "text/*",

Duration as seconds

DurationSeconds

This example is not tested
// Rust
#[serde_as(as = "serde_with::DurationSeconds<u64>")]
value: Duration,

// JSON
"value": 86400,

DurationSecondsWithFrac supports subsecond precision:

This example is not tested
// Rust
#[serde_as(as = "serde_with::DurationSecondsWithFrac<f64>")]
value: Duration,

// JSON
"value": 1.234,

Different serialization formats are possible:

This example is not tested
// Rust
#[serde_as(as = "serde_with::DurationSecondsWithFrac<String>")]
value: Duration,

// JSON
"value": "1.234",

The same conversions are also implemented for chrono::Duration with the chrono feature.

Ignore deserialization errors

Check the documentation for DefaultOnError.

Maps to Vec of tuples

This example is not tested
// Rust
#[serde_as(as = "Vec<(_, _)>")]
value: HashMap<String, u32>, // also works with BTreeMap

// JSON
"value": [
    ["hello", 1],
    ["world", 2]
],

The inverse operation is also available.

NaiveDateTime like UTC timestamp

Requires the chrono feature.

This example is not tested
// Rust
#[serde_as(as = "chrono::DateTime<chrono::Utc>")]
value: chrono::NaiveDateTime,

// JSON
"value": "1994-11-05T08:15:30Z",
                             ^ Pretend DateTime is UTC

None as empty String

NoneAsEmptyString

This example is not tested
// Rust
#[serde_as(as = "serde_with::NoneAsEmptyString")]
value: Option<String>,

// JSON
"value": "", // converts to None

"value": "Hello World!", // converts to Some

Timestamps as seconds since UNIX epoch

[TimestampSeconds]

This example is not tested
// Rust
#[serde_as(as = "serde_with::TimestampSeconds<i64>")]
value: SystemTime,

// JSON
"value": 86400,

[TimestampSecondsWithFrac] supports subsecond precision:

This example is not tested
// Rust
#[serde_as(as = "serde_with::TimestampSecondsWithFrac<f64>")]
value: SystemTime,

// JSON
"value": 1.234,

Different serialization formats are possible:

This example is not tested
// Rust
#[serde_as(as = "serde_with::TimestampSecondsWithFrac<String>")]
value: SystemTime,

// JSON
"value": "1.234",

The same conversions are also implemented for chrono::DateTime<Utc> and chrono::DateTime<Local> with the chrono feature.

Value into JSON String

Some JSON APIs are weird and return a JSON encoded string in a JSON response

JsonString

Requires the json feature.

This example is not tested
// Rust
#[derive(Deserialize, Serialize)]
struct OtherStruct {
    value: usize,
}

#[serde_as(as = "serde_with::json::JsonString")]
value: OtherStruct,

// JSON
"value": "{\"value\":5}",

Vec of tuples to Maps

This example is not tested
// Rust
#[serde_as(as = "HashMap<_, _>")] // also works with BTreeMap
value: Vec<String, u32>,

// JSON
"value": {
    "hello": 1,
    "world": 2
},

The inverse operation is also available.