1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
# Custom de/serialization functions for Rust's [serde](https://serde.rs)
[![crates.io badge](https://img.shields.io/crates/v/serde_with.svg)](https://crates.io/crates/serde_with/)
[![Build Status](https://github.com/jonasbb/serde_with/workflows/Rust%20CI/badge.svg)](https://github.com/jonasbb/serde_with)
[![codecov](https://codecov.io/gh/jonasbb/serde_with/branch/master/graph/badge.svg)](https://codecov.io/gh/jonasbb/serde_with)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4322/badge)](https://bestpractices.coreinfrastructure.org/projects/4322)
[![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/py7ida)
---
This crate provides custom de/serialization helpers to use in combination with [serde's with-annotation][with-annotation] and with the improved [`serde_as`][as-annotation]-annotation.
Some common use cases are:
* De/Serializing a type using the `Display` and `FromStr` traits, e.g., for `u8`, `url::Url`, or `mime::Mime`.
Check [`DisplayFromStr`] for details.
* Support for arrays larger than 32 elements or using const generics.
With `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](#large-and-const-generic-arrays).
* Skip serializing all empty `Option` types with [`#[skip_serializing_none]`][skip_serializing_none].
* Apply a prefix to each field name of a struct, without changing the de/serialize implementations of the struct using [`with_prefix!`][].
* Deserialize a comma separated list like `#hash,#tags,#are,#great` into a `Vec<String>`.
Check the documentation for [`serde_with::StringWithSeparator::<CommaSeparator, T>`][StringWithSeparator].
### Getting Help
**Check out the [user guide][user guide] to find out more tips and tricks about this crate.**
For further help using this crate you can [open a new discussion](https://github.com/jonasbb/serde_with/discussions/new) or ask on [users.rust-lang.org](https://users.rust-lang.org/).
For bugs, please open a [new issue](https://github.com/jonasbb/serde_with/issues/new) on GitHub.
## Use `serde_with` in your Project
```bash
# 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.
## Examples
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`
[![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/py7ida)
```rust
#[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"}
```
### Large and const-generic arrays
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.
[![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/um0xyi)
```rust
#[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]`.
[![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/xr1tm0)
```rust
#[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}
```
### Advanced `serde_as` usage
This example is mainly supposed to highlight the flexibility of the `serde_as`-annotation compared to [serde's with-annotation][with-annotation].
More details about `serde_as` can be found in the [user guide].
```rust
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"
}
}
}
```
[`DisplayFromStr`]: https://docs.rs/serde_with/3.0.0/serde_with/struct.DisplayFromStr.html
[`with_prefix!`]: https://docs.rs/serde_with/3.0.0/serde_with/macro.with_prefix.html
[feature flags]: https://docs.rs/serde_with/3.0.0/serde_with/guide/feature_flags/index.html
[skip_serializing_none]: https://docs.rs/serde_with/3.0.0/serde_with/attr.skip_serializing_none.html
[StringWithSeparator]: https://docs.rs/serde_with/3.0.0/serde_with/struct.StringWithSeparator.html
[user guide]: https://docs.rs/serde_with/3.0.0/serde_with/guide/index.html
[with-annotation]: https://serde.rs/field-attrs.html#with
[as-annotation]: https://docs.rs/serde_with/3.0.0/serde_with/guide/serde_as/index.html
## License
Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
## Contribution
For detailed contribution instructions please read [`CONTRIBUTING.md`].
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
[`CONTRIBUTING.md`]: https://github.com/jonasbb/serde_with/blob/master/CONTRIBUTING.md
|