I am deserializing some JSON objects which come in as requests. The input body is nested, but a certain field is sometimes misformatted for a variety of reasons. In that situati
certain field is sometimes misformatted
You didn't say how malformed the incoming JSON was. Assuming it's still valid JSON, you can pull this off with Serde's struct flatten and customized deserialization:
The customized deserialization is done in a way that never fails for valid JSON input, although it may not return value of expected type if the input has unexpected format.
But these unexpected fields still need to go somewhere. Serde's struct flatten
comes in handy here to catch them since any JSON snippet can be deserialized to a HashMap
.
//# serde = { version = "1.0.103", features = ["derive"] }
//# serde_json = "1.0.44"
use serde::{Deserialize, Deserializer, de::DeserializeOwned};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Deserialize, Debug)]
struct A {
keep_this: Foo,
trouble: SometimesBad,
}
#[derive(Deserialize, Debug)]
struct Foo {
foo: i32,
}
#[derive(Deserialize, Debug)]
struct SometimesBad {
inner: TryParse,
#[serde(flatten)]
blackhole: HashMap,
}
#[derive(Deserialize, Debug)]
struct Bar {
bar: String,
}
#[derive(Debug)]
enum TryParse {
Parsed(T),
Unparsed(Value),
NotPresent
}
impl<'de, T: DeserializeOwned> Deserialize<'de> for TryParse {
fn deserialize>(deserializer: D) -> Result {
match Option::::deserialize(deserializer)? {
None => Ok(TryParse::NotPresent),
Some(value) => match T::deserialize(&value) {
Ok(t) => Ok(TryParse::Parsed(t)),
Err(_) => Ok(TryParse::Unparsed(value)),
},
}
}
}
fn main() {
let valid = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "bar": "one"}}}"#;
println!("{:#?}", serde_json::from_str::(valid));
let extra_field = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "bar": "one"}, "extra": 2019}}"#;
println!("{:#?}", serde_json::from_str::(extra_field));
let wrong_type = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "bar": 1}}}"#;
println!("{:#?}", serde_json::from_str::(wrong_type));
let missing_field = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "baz": "one"}}}"#;
println!("{:#?}", serde_json::from_str::(missing_field));
let missing_inner = r#"{ "keep_this": { "foo": 1 }, "trouble": { "whatever": { "bar": "one"}}}"#;
println!("{:#?}", serde_json::from_str::(missing_inner));
}
(The credit isn't all mine. Serde's issue 1583 basically has everything.)