rust - 使用 Serde 反序列化时如何忽略额外的元组项? ("trailing characters"错误)

标签 rust serde

Serde 在反序列化为常规结构时忽略未知的命名字段。在反序列化为元组结构(例如来自异构 JSON 数组)时,我如何同样忽略额外的项目?

例如,这段代码忽略了额外的 "c" 字段就好了:

#[derive(Serialize, Deserialize, Debug)]
pub struct MyStruct { a: String, b: i32 }

fn test_deserialize() -> MyStruct {
    ::serde_json::from_str::<MyStruct>(r#"
    {
        "a": "foo",
        "b": 123,
        "c": "ignore me"
    }
    "#).unwrap()
}
// => MyStruct { a: "foo", b: 123 }

相比之下,这对元组中的额外项目失败了:

#[derive(Serialize, Deserialize, Debug)]
pub struct MyTuple(String, i32);

fn test_deserialize_tuple() -> MyTuple {
    ::serde_json::from_str::<MyTuple>(r#"
        [
            "foo",
            123,
            "ignore me"
        ]
    "#).unwrap()
}
// => Error("trailing characters", line: 5, column: 13)

我想在我的数据格式中允许额外的项目以实现向前兼容性。让 Serde 在反序列化时忽略额外元组项的最简单方法是什么?

最佳答案

您可以实现一个忽略序列其余部分的自定义 Visitor。请注意,必须消耗整个序列。这是一个重要的部分(尝试删除它,你会得到同样的错误):

// This is very important!
while let Some(IgnoredAny) = seq.next_element()? {
    // Ignore rest
}

这是一个工作示例:

use std::fmt;

use serde::de::{self, Deserialize, Deserializer, IgnoredAny, SeqAccess, Visitor};
use serde::Serialize;

#[derive(Serialize, Debug)]
pub struct MyTuple(String, i32);

impl<'de> Deserialize<'de> for MyTuple {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct MyTupleVisitor;

        impl<'de> Visitor<'de> for MyTupleVisitor {
            type Value = MyTuple;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct MyTuple")
            }

            fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
            where
                V: SeqAccess<'de>,
            {
                let s = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
                let n = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::invalid_length(1, &self))?;

                // This is very important!
                while let Some(IgnoredAny) = seq.next_element()? {
                    // Ignore rest
                }

                Ok(MyTuple(s, n))
            }
        }

        deserializer.deserialize_seq(MyTupleVisitor)
    }
}

fn main() {
    let two_elements = r#"["foo", 123]"#;
    let three_elements = r#"["foo", 123, "bar"]"#;

    let tuple: MyTuple = serde_json::from_str(two_elements).unwrap();
    assert_eq!(tuple.0, "foo");
    assert_eq!(tuple.1, 123);

    let tuple: MyTuple = serde_json::from_str(three_elements).unwrap();
    assert_eq!(tuple.0, "foo");
    assert_eq!(tuple.1, 123);
}

关于rust - 使用 Serde 反序列化时如何忽略额外的元组项? ("trailing characters"错误),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57529456/

相关文章:

rust - 派生 Serde 的序列化或反序列化强制泛型类型可序列化,尽管它不需要是

rust - Rust 书中的 stdio 示例使用不稳定的库功能 'old_io' - 存在哪些替代方案?

rust - 如何为具有 "rented"引用的类型实现特征

multithreading - 在线程中使用特征方法

rust - Visitor 特性如何只允许部分实现?

rust - 我可以使用自己的序列化结构在 Rocket 中渲染模板吗?

xml - 使用serde序列化结构时,如何展平 `Vec`字段?

rust - 有没有一种方法可以为与基础存储不匹配的类型创建可变引用?

rust - 在参数化函数中复制/克隆向量的惯用 Rust 方法是什么?

rust - 使用 Serde 反序列化可能为空的字符串