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

百般思念 提交于 2020-11-29 09:40:05

问题


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 situation I still want the rest of the object. This doesn't all have to be done through serde; but what is happening now, is that if a single subfield is messed up, the whole request is trashed. I want to somehow still deserialize that result and just mark the field as errored out. How can this be done?

E.g. the data schema might look like:

struct BigNested {
    a: Vec<A>,
    b: B, // definition omitted
}

struct A {
    keep_this: Foo,
    trouble: SometimesBad,
}

trouble is the field that's frequently coming in messed up. I would be happy to (e.g.) turn trouble into a Result<SometimesBad, Whatever> and process it from there, but I don't know how to get serde to let me do that.


回答1:


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.)



来源:https://stackoverflow.com/questions/59254690/how-to-use-serde-to-parse-a-field-that-might-fail-to-be-deserialized-without-fai

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!