web-dev-qa-db-ja.com

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に余分なタプルアイテムを無視させる最も簡単な方法は何ですか?

10
Jo Liss

シーケンスの残りを無視するカスタム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);
}
8
zrzka

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(())
}

以下も参照してください。

0
Shepmaster