Десериализовать в структуру с членом перечисления

Я пытаюсь взять json, который выглядит так:

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

и используйте serde для десериализации в такую ​​структуру данных:

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

}

pub enum Foo {
    Bar,
    Baz,
}

У меня есть код, но, честно говоря, это довольно просто из serde руководства по «десериализации без макросов», и я не совсем уверен, что мне нужно сделать, чтобы заставить его десериализовать поле foo в Foo.

Я реализовал Deserialize для Foo перечисления, которого, как я думал, будет достаточно для вызова visitor.visit_value() в моем impl serde::de::Vistor for QuuxVisitor, чтобы вызвать эту версию deserialize, но, похоже, это не так.

Ошибка, которую я получаю при попытке десериализации до Quux, равна called 'Result::unwrap()' on an 'Err' value: SyntaxError("expected value", 2, 20), но если я изменю Quux на использование String для foo вместо Foo, десериализация будет выполнена нормально.


person Community    schedule 01.02.2016    source источник


Ответы (3)


Есть полный пример для 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)
    }
}

Вы можете увидеть похожий пример здесь.

person DenisKolodin    schedule 14.04.2017

Вот пример. Я не уверен, как лучше всего обрабатывать неизвестные поля, но это работает:

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

Я использовал Rust 1.6.

person Tibor Benke    schedule 01.02.2016
comment
Да, это приведет к десериализации перечисления, но когда структура имеет член, имеющий тип enum Foo, десериализация структуры не использует Deserialize impl для Foo. - person ; 01.02.2016

Я бы рекомендовал использовать serde_derive для создания Deserialize реализаций, а не записывать их вручную.

В приведенном ниже коде я использую #[serde(rename_all = "lowercase")], чтобы Foo принимал JSON-представления "bar" и "baz" вместо используемых по умолчанию "Bar" и "Baz", соответствующих их заглавным буквам в коде Rust.

#[macro_use]
extern crate serde_derive;
extern crate serde;

#[derive(Deserialize, Debug)]
struct Quux {
    foo: Foo,
    name: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
enum Foo {
    Bar,
    Baz,
}

fn main() {
    let j = r#"
        {
            "foo": "bar",
            "name": "some name"
        }
        "#;

    println!("{:#?}", serde_json::from_str::<Quux>(j).unwrap());
}
person dtolnay    schedule 12.11.2018