
HNil
с нужной нам логикой? Так как JSON — самоописываемый формат в том смысле, что мы можем посмотреть на начало входа и решить, как парсить остаток — мы можем вызвать у десериализатора метод dezerialize_any
, что фактически означает "посмотри данные и реши сам, как их разбирать". Нам также потребуется посетитель, который для разбора подчастей JSON-документа будет вызывать вне зависимости от запрашиваемого типа deserialize_any
у десериализатора и не будет при это сохранять никаких данных. К счастью, в составе serde уже есть такой тип — IgnoredAny — так что мы можем просто использовать его:impl<'de> Deserialize<'de> for HNil {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>
{
deserializer.deserialize_any(serde::de::IgnoredAny)?;
Ok(Self)
}
}
Как видите, код проверяет, что формат корректен, но не вынимает реально из него никаких данных — ровно то, что нам и было нужно.Теперь мы сделаем несколько структур для десериализации — и макрос для более удобной деконструкции
HList
:macro_rules! hlist_pat {Сконструируем вход, содержащий все поля:
() => { HNil };
($head:pat $(, $rest:pat)* $(,)?) => { HCons { head: $head, tail: hlist_pat!($($rest),*) } };
}
#[derive(Deserialize)]
struct Items {
items: Vec<String>
}
#[derive(Deserialize)]
struct Version {
version: u32,
}
#[derive(Deserialize)]
struct User {
user_id: String,
}
let json = r#"{
"user_id": "john_doe",
"items": ["salad", "juice", "beer", "fork"],
"version": 0,
"bogus": [null, 0.3, 4, "nope", {}]
}"#;
И попробуем вытащить всё, что нас интересует:use serde_json::from_str as from_json;
let hlist_pat!(user, items, version) =
from_json::<HList![User, Items, Version]>(json).unwrap();
assert_eq!(user.user_id, "john_doe");
assert_eq!(items.items, ["salad", "juice", "beer", "fork"]);
assert_eq!(version.version, 0);
Теперь уберём, скажем, версию:let json = r#"{
"user_id": "john_doe",
"items": ["salad", "juice", "beer", "fork"],
"bogus": [null, 0.3, 4, "nope", {}]
}"#;
assert!(from_json::<HList![User, Items, Version]>(json).is_err());
assert!(from_json::<HList![User, Items ]>(json).is_ok());
Отсутствие user_id
и items
вызовет ошибку, но не в том случае, если требуемые наборы полей опциональны:let json = r#"{Ну и, разумеется, мы можем вытащить все остальные поля, что есть:
"version": 0,
"bogus": [null, 0.3, 4, "nope", {}]
}"#;
assert!(from_json::<HList![ User, Items, Version]>(json).is_err());
assert!(from_json::<HList![Option<User>, Option<Items>, Version]>(json).is_ok());
type Rest = serde_json::Map<String, serde_json::Value>;
let json = r#"{
"user_id": "john_doe",
"items": ["salad", "juice", "beer", "fork"],
"version": 0,
"bogus": [null, 0.3, 4, "nope", {}]
}"#;
let hlist_pat!(_, _, _, rest) = from_json::<HList![User, Items, Version, Rest]>(json).unwrap();
assert_eq!(
serde_json::Value::Object(rest),
serde_json::json!({
"bogus": [null, 0.3, 4, "nope", {}],
})
);
По моему, вышло красиво и изящно. Как всегда, весь код в гисте.