Deserialize a Vec of values into a struct by implementing serde `Deserializer`

Viewed 884

I have a data format using custom enum of values. From the database I receive a Vec<MyVal>. I want to convert this to a struct (and fail if it doesn't work). I want to use serde because after processing I want to return the API response as a json, and serde makes this super easy.

Playground link for the example

enum MyVal {
  Bool(bool),
  Text(String)
}

#[derive(Serialize, Deserialize)]
struct User {
  name: String,
  registered: bool
}

The challenge is with converting the data format into the serde data model. For this I can implement a Deserializer and implement the visit_seq method i.e. visit the Vec<MyVal> as if it were a sequence and return the values one by one. The visitor for User can consume the visited values to build the struct User.

However I'm not able to figure out how to convert the Vec into something visitor_seq can consume. Here's some sample code.

struct MyWrapper(Vec<MyVal>);

impl<'de> Deserializer<'de> for MyWrapper {
    type Error = de::value::Error;

    // skip unncessary

    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: serde::de::Visitor<'de>,
    {
        let convert_myval = move |myval: &MyVal| match myval {
            MyVal::Bool(b) => visitor.visit_bool(*b),
            MyVal::Text(s) => visitor.visit_string(s.clone())
        };

        // now I have a vec of serde values
        let rec_vec: Vec<Result<V::Value, Self::Error>> =
            self.0.iter().map(|myval| convert_myval(myval)).collect();
        // giving error!!
        visitor.visit_seq(rec_vec.into_iter())
    }
}

The error is

92   |         visitor.visit_seq(rec_vec.into_iter())
     |                 --------- ^^^^^^^^^^^^^^^^^^^ the trait `SeqAccess<'de>` is not implemented for `std::vec::IntoIter<Result<<V as Visitor<'de>>::Value, _::_serde::de::value::Error>>`
     |                 |
     |                 required by a bound introduced by this call

So I looked into SeqAccess and it has an implementor that requires that whatever is passed to it implement the Iterator trait. But I thought I had that covered, because vec.into_iter returns an IntoIter, a consuming iterator which does implement the Iterator trait.

So I'm completely lost as to what's going wrong here. Surely there must be a way to visit a Vec<Result<Value, Error>> as a seq?

1 Answers

Preface: The question wants to treat a Rust data structure Vec<MyData> like a serialized piece of data (e.g.: like a JSON string) and allow deserializing that into any other Rust data structure that implements Deserialize. This is a quite unusual, but not without precedent. And since the MyVals are actually pieces of data with various types which get returned from a database access crate, this approach does make sense.

The main problem with the code in the question is that it tries to deserialize two different data structures (MyWrapper<Vec<MyVal>> and MyVal) with a single Deserializer. The obvious way out is to define a second struct MyValWrapper(MyVal) and implement Deserializer for it:

struct MyValWrapper(MyVal);
impl<'de> Deserializer<'de> for MyValWrapper {
    type Error = de::value::Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: de::Visitor<'de>,
    {
        match self.0 {
            MyVal::Bool(b) => visitor.visit_bool(b),
            MyVal::Text(s) => visitor.visit_string(s.clone()),
        }
    }

    // Maybe you should call deserialize_any from all other deserialize_* functions to get a passable error message, e.g.:
    forward_to_deserialize_any! {
        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
        bytes byte_buf option unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }
    // Though maybe you want to handle deserialize_enum differently...
}

MyValWrapper now can deserialize a MyVal. To use MyValWrapper to deserialize a Vec of MyVals, the serde::value::SeqDeserializer adapter is convenient, it can turn an iterator of (Into)Deserializer into a sequence Deserializer:

let data: Vec<MyData> = …;
// Vec deserialization snippet
let mut sd = SeqDeserializer::new(data.into_iter().map(|myval| MyValWrapper(myval)));
let res = visitor.visit_seq(&mut sd)?;
sd.end()?;
Ok(res)

For some reason, SeqDeserializer requires the iterator items to be IntoDeserializer, but no impl<T: IntoDeserializer> Deserializer for T exists, so we need to make sure that MyValWrapper is not only a Deserializer but trivially also an IntoDeserializer:

impl<'de> IntoDeserializer<'de> for MyValWrapper {
    type Deserializer = MyValWrapper;
    fn into_deserializer(self) -> Self::Deserializer {
        self
    }
}

Finally, you need to impl Deserializer for MyWrapper (you can use the "Vec deserialization snippet" for that) — if you do actually need Deserializer to be implemented, which I suspect you don't: the SeqDeserializer already implements Deserializer, and it is a wrapper struct (just as MyWrapper is a wrapper struct). Especially, if your final goal is having a function like

fn turn_bad_myvals_into_good_T<T: DeserializeOwned>(v: Vec<MyVal>) -> T {
    T::deserialize(SeqDeserializer::new(
        v.into_iter().map(|myval| MyValWrapper(myval)),
    ))
}

then you entirely don't need MyWrapper.

Related