Struct flattening
The flatten
attribute inlines keys from a field into the parent struct.
flatten
may be used any number of times within the same struct. It is
supported only within structs that have named fields, and the field to which it
is applied must be a struct or map type.
Note: flatten
is not supported in combination with structs that use
deny_unknown_fields
. Neither the outer nor inner flattened struct should use
that attribute.
The flatten
attribute serves the following two common use cases:
Factor out frequently grouped keys
Consider a paginated API which returns a page of results along with pagination metadata that identifies how many results were requested, how far into the total set of results we are looking at, and how many results exist in total. If we are paging through a total of 1053 results 100 at a time, the third page may look like this.
{
"limit": 100,
"offset": 200,
"total": 1053,
"users": [
{"id": "49824073-979f-4814-be10-5ea416ee1c2f", "username": "john_doe"},
...
]
}
This same scheme with "limit"
and "offset"
and "total"
fields may be
shared across lots of different API queries. For example we may want paginated
results when querying for users, for issues, for projects, etc.
In this case it can be convenient to factor the common pagination metadata fields into a shared struct that can be flattened into each API response object.
#[derive(Serialize, Deserialize)]
struct Pagination {
limit: u64,
offset: u64,
total: u64,
}
#[derive(Serialize, Deserialize)]
struct Users {
users: Vec<User>,
#[serde(flatten)]
pagination: Pagination,
}
Capture additional fields
A field of map type can be flattened to hold additional data that is not captured by any other fields of the struct.
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use serde_json::Value;
#[derive(Serialize, Deserialize)]
struct User {
id: String,
username: String,
#[serde(flatten)]
extra: HashMap<String, Value>,
}
For example if we fill the flattened extra
field with the key "mascot":
"Ferris"
, it would serialize to the following JSON representation.
{
"id": "49824073-979f-4814-be10-5ea416ee1c2f",
"username": "john_doe",
"mascot": "Ferris"
}
Deserialization of this data would populate "mascot"
back into the flattened
extra
field. This way additional data in an object can be collected for later
processing.