使用Serde反序列化时,如何忽略多余的元组项? (“尾随字符”错误)

时间:2019-08-16 18:27:34

标签: 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在反序列化时忽略多余的元组项的最简单方法是什么?

2 个答案:

答案 0 :(得分:8)

您可以实现一个自定义的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);
}

答案 1 :(得分:0)

对于JSON,我结合了RawValue和自定义反序列化:

use serde::{Deserialize, Deserializer};

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

#[derive(Deserialize, Debug)]
struct MyTupleFutureCompat<'a>(
    String,
    i32,
    #[serde(default, borrow)] Option<&'a serde_json::value::RawValue>,
);

impl<'de> Deserialize<'de> for MyTuple {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let t: MyTupleFutureCompat = Deserialize::deserialize(deserializer)?;
        Ok(MyTuple(t.0, t.1))
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let json = r#"[
        "foo",
        123,
        "ignore me"
    ]"#;
    let d: MyTuple = serde_json::from_str(json)?;
    println!("{:?}", d);
    Ok(())
}

另请参阅: