Created
September 2, 2021 21:11
-
-
Save AnthonyMikh/667929e30fddfe08026305d91a60bbe5 to your computer and use it in GitHub Desktop.
Extracting arbitrary field sets from JSON
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use serde::Deserialize; | |
struct HNil; | |
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) | |
} | |
} | |
#[derive(Deserialize)] | |
struct HCons<H, T> { | |
#[serde(flatten)] | |
head: H, | |
#[serde(flatten)] | |
tail: T, | |
} | |
macro_rules! HList { | |
() => { HNil }; | |
($head:ty $(, $rest:ty)* $(,)?) => { HCons<$head, HList![$($rest),*]> }; | |
} | |
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, | |
} | |
fn main() { | |
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()); | |
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", {}], | |
}) | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment