error-handling - 如何在不使整个反序列化失败的情况下使用 Serde 解析可能无法反序列化的字段?

标签 error-handling rust deserialization serde

我正在反序列化一些作为请求传入的 JSON 对象。输入正文是嵌套的,但是某个字段有时会因为各种原因格式错误。在那种情况下,我仍然想要该对象的其余部分。这并不都必须通过 serde 来完成;但现在正在发生的事情是,如果一个子字段被搞砸了,整个请求都会被丢弃。我想以某种方式仍然反序列化该结果,并将该字段标记为错误。如何做到这一点?

例如数据模式可能如下所示:

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

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

trouble是经常出现困惑的领域。我很乐意(例如)转 trouble进入Result<SometimesBad, Whatever>并从那里处理它,但我不知道如何让 serde 让我这样做。

最佳答案

certain field is sometimes misformatted

您没有说明传入的 JSON 格式有多么畸形。假设它仍然是有效的 JSON,您可以使用 Serde's struct flatten 来解决这个问题和自定义反序列化:

  • 自定义反序列化以一种永远不会对有效 JSON 输入失败的方式完成,但如果输入具有意外格式,它可能不会返回预期类型的​​值。

  • 但这些意想不到的领域仍然需要去某个地方。 Serde 的结构 flatten因为 任何 JSON 片段都可以反序列化为 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));
}

(功劳不全是我的。Serde's issue 1583基本上什么都有。)

关于error-handling - 如何在不使整个反序列化失败的情况下使用 Serde 解析可能无法反序列化的字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64114043/

相关文章:

java - (Gson) JSON 到包含 ArrayList<Class> 的 Java 对象

javascript - 抛出自定义错误 async/await try-catch

c fscanf 错误检查

ruby-on-rails - ActiveResource 错误处理

java - 括号和数组声明的问题

rust - 根据值从 HashMap 中删除条目

rust - 如何编写将代码注入(inject)函数的自定义属性

java - 使用 Jackson 反序列化树形结构 JSON 中的值

c++ - 在 perl 和 cpp 之间共享数据结构

rust - Rust 结构定义中的最后一个逗号是必要的吗?