How to use Serde to parse a field that might fail to be deserialized without failing the entire deserialization?

前端 未结 1 1286
忘了有多久
忘了有多久 2021-01-22 00:37

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

相关标签:
1条回答
  • 2021-01-22 01:35

    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<String, Value>.

    //# 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<Bar>,
    
        #[serde(flatten)]
        blackhole: HashMap<String, Value>,
    }
    
    #[derive(Deserialize, Debug)]
    struct Bar {
        bar: String,
    }
    
    #[derive(Debug)]
    enum TryParse<T> {
        Parsed(T),
        Unparsed(Value),
        NotPresent
    }
    
    impl<'de, T: DeserializeOwned> Deserialize<'de> for TryParse<T> {
        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
            match Option::<Value>::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::<A>(valid));
    
        let extra_field = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "bar": "one"}, "extra": 2019}}"#;
        println!("{:#?}", serde_json::from_str::<A>(extra_field));
    
        let wrong_type = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "bar": 1}}}"#;
        println!("{:#?}", serde_json::from_str::<A>(wrong_type));
    
        let missing_field = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "baz": "one"}}}"#;
        println!("{:#?}", serde_json::from_str::<A>(missing_field));
    
        let missing_inner = r#"{ "keep_this": { "foo": 1 }, "trouble": { "whatever": { "bar": "one"}}}"#;
        println!("{:#?}", serde_json::from_str::<A>(missing_inner));
    }
    

    (The credit isn't all mine. Serde's issue 1583 basically has everything.)

    0 讨论(0)
提交回复
热议问题