Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize to struct with an enum member

I am trying to take some json that looks like this:

{
    "foo": "bar",
    "name": "some name"
}

and use serde to deserialize it to a data structure like this:

#[derive(Clone, PartialEq, Debug)]
pub struct Quux {
    foo: Foo,
    name: String,

}

pub enum Foo {
    Bar,
    Baz,
}

I have some code, but to be honest, it is pretty much straight out of the serde guide to "deserializing without macros," and I'm not sure exactly what I would need to do to get it to deserialize the foo field to a Foo.

I have implemented Deserialize for the Foo enum, which I thought would be enough for the visitor.visit_value() call in my impl serde::de::Vistor for QuuxVisitor to call that version of deserialize, but does not seem to be the case.

The error I get when I try to deserialize to Quux is called 'Result::unwrap()' on an 'Err' value: SyntaxError("expected value", 2, 20), but if I change Quux to use a String for foo instead of a Foo, it deserializes fine.


2 Answers

There is a full example for Rust 1.18 / serde 1.0.0:

impl<'de> Deserialize<'de> for EventKind {
    fn deserialize<D>(deserializer: D) -> result::Result<EventKind, D::Error>
        where D: Deserializer<'de>
    {
        struct FieldVisitor {
            min: usize,
        };

        impl<'de> Visitor<'de> for FieldVisitor {
            type Value = EventKind;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(formatter, "a string containing at least {} bytes", self.min)
            }

            fn visit_str<E>(self, value: &str) -> result::Result<EventKind, E>
                where E: serde::de::Error
            {
                let kind = match value {
                    "request" => EventKind::Request,
                    "ready" => EventKind::Ready,
                    "next" => EventKind::Next,
                    "reject" => EventKind::Reject,
                    "fail" => EventKind::Fail,
                    "done" => EventKind::Done,
                    "cancel" => EventKind::Cancel,
                    "suspended" => EventKind::Suspended,
                    s => {
                        return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s),
                                                                   &self));
                    }
                };
                Ok(kind)
            }
        }
        deserializer.deserialize_str(FieldVisitor { min: 4 })
    }
}

enum EventKind {
    Request,
    Ready,
    Next,
    Reject,
    Fail,
    Done,
    Cancel,
    Suspended,
}

impl Serialize for EventKind {
    fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
        where S: Serializer
    {
        let kind = match *self {
            EventKind::Request => "request",
            EventKind::Ready => "ready",
            EventKind::Next => "next",
            EventKind::Reject => "reject",
            EventKind::Fail => "fail",
            EventKind::Done => "done",
            EventKind::Cancel => "cancel",
            EventKind::Suspended => "suspended",
        };
        serializer.serialize_str(kind)
    }
}

You can see a similar example here.

like image 94
17 revs, 13 users 59% Avatar answered Sep 16 '25 17:09

17 revs, 13 users 59%


Here is an example. I'm not sure about the best way to handle unknown fields, but this works:

extern crate serde;

use serde::de::{Deserialize, Deserializer, Visitor, Error};

pub enum Foo {
    Bar,
    Baz,
}

impl Deserialize for Foo {
    fn deserialize<D>(deserializer: &mut D) -> Result<Foo, D::Error>
        where D: Deserializer
    {
        struct FieldVisitor;

        impl Visitor for FieldVisitor {
            type Value = Foo;

            fn visit_str<E>(&mut self, value: &str) -> Result<Foo, E>
                where E: Error
            {
                match value {
                    "bar" => Ok(Foo::Bar),
                    "baz" => Ok(Foo::Baz),
                    _ => Err(E::syntax(&format!("Unexpected field: {}", value))),
                }
            }
        }

        deserializer.visit(FieldVisitor)
    }
}

I used Rust 1.6.

like image 31
Tibor Benke Avatar answered Sep 16 '25 17:09

Tibor Benke