Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I ignore extra tuple items when deserializing with Serde? ("trailing characters" error)

Tags:

rust

serde

Serde ignores unknown named fields when deserializing into regular structs. How can I similarly ignore extra items when deserializing into tuple structs (e.g. from a heterogeneous JSON array)?

For example, this code ignores the extra "c" field just fine:

#[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 }

By contrast, this fails on the extra item in the tuple:

#[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)

I'd like to allow extra items for forward compatibility in my data format. What's the easiest way to get Serde to ignore extra tuple items when deserializing?

like image 238
Jo Liss Avatar asked Aug 16 '19 18:08

Jo Liss


1 Answers

You can implement a custom Visitor which ignores rest of the sequence. Be aware that the whole sequence must be consumed. This is an important part (try to remove it and you'll get same error):

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

Here's a working example:

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);
}
like image 144
zrzka Avatar answered Oct 07 '22 12:10

zrzka