[serde] How to delegate deserialization to a generic `Deserialize`able in a visitor method?


#1

Also posted on https://github.com/serde-rs/serde/issues/146.

I haven’t figured out how I can achieve the following using serde, therefore I appreciate your help.

Introduction

The data structure I want to deserialize appears in two forms. It contains either a single string or an array of objects.

["This string maps to the error variant"]
[{keys: values}, {keys: values}, …]

The enum VariantData<T> should represent this data structure where T is a generic Deserializeable encapsulating the knowlegde how to deserialize itself.

enum VariantData<T> {
    Content(Vec<T>),
    Error(String),
}

In VecVisitor<T> calling SeqVisitor::visit() is enough, because every element is of type T and the method delegates deserialization to T.

I can use the same approach for VariantData<T> deserializing a single element to a helper enum FirstValue<T>. I guess an alternative would be to use a EnumVisitor directly but I’m not yet familiar enough with it.

enum FirstValue<T> {
    Content(T),
    Error(String),
}

The problem

In both cases I need to distinguish the type I want deserialize to based on the kind of structure in the raw data. If a map follows the target type is T (or Vec<T> if directly serializing the array instead of looping over it), if a plain string follows the target type is String.

My first idea was: (The code is incomplete since its enough to show my problem.)

impl<T> Deserialize for VariantContent<T> {
    fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Variant<T>, D::Error> {
        struct VariantContentVisitor<T> {
            data: PhantomData<T>,
        }

        impl<T> de::Visitor for VariantContentVisitor<T> where T: Deserialize {
            type Value = VariantContent<T>;

            fn visit_seq<V>(&mut self, mut visitor: V) -> Result<Self, V::Error>
                where V: de::SeqVisitor
            {
                let first: FirstValue<T> = try!(visitor.visit());
                match first {
                    FirstValue::Content(x) => {
                        // Loop over the remaining Ts and add them to `Vec<T>`
                        let elem: T = try!(visitor.visit());
                        unimplemented!()
                    },
                    FirstValue::Error(x) => VariantContent::Error(x),
                }
            }
        }

        deserializer.visit(VariantContentVisitor { data: PhantomData })
    }
}

// …
impl<T> de::Visitor for FirstValueVisitor<T> where T: Deserialize {
    type Value = FirstValue<T>;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Self, V::Error>
        where V: de::MapVisitor
    {
        // Deserialize a T. This is called for the first element.
        unimplemented!()
    }

    fn visit_string(&mut self, value: String) -> Result<Self, V::Error> {
        FirstValue::Error(value)
    }
}
// …

Now my question: How can I delegate the deserialization of a T to it’s Deserialize implementation in visit_map()? It is a generic type, I can’t know how to deserialize that type correctly. As mentioned above I also can’t guide SeqVisitor::visit to the correct type directly, because I need to know if a map or a string follows next.

I also tried to use a EnumVisitor:

impl<O> Deserialize for VariantContent<O> {
    fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Variant<O>, D::Error> {
        struct VariantContentVisitor<O> {
            data: PhantomData<O>,
        }

        impl<O> de::EnumVisitor for VariantContentVisitor<O> where O: Deserialize {
            type Value = VariantContent<O>;

            fn visit<V>(&mut self, visitor: V) -> Result<Self::Value, V::Error> {
                // How to determine which variant to decode?
            }
        }

        deserializer.visit_enum("VariantContent",
                                &["Content", "Error"],
                                VariantContentVisitor { data: PhantomData })
    }
}

It is shorter than my first attempt, but how can I determine which type (T or String) I need to deserialize to? It depends on the kind of the following data, map or primitive string.

Thanks for your help!