Как настроить реализацию десериализации для определенных типов ввода, но не для всех?

У меня есть такой тип, хотя на самом деле мой тип больше и сложнее:

struct MyType {
    i: u32,
}

Если я реализую Deserialize для этого типа, serde будет искать что-то вроде этого (меня интересует JSON):

{"i":100}  

Я хочу настроить его так, чтобы я мог десериализовать также из массива байтов:

[1, 2, 3, 4]

Я могу написать impl для обработки массива, но я хочу, чтобы serde автоматически сгенерировал остальное (что будет visit_map):

impl<'de> Deserialize<'de> for MyType {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct MyTypeVisitor;

        impl<'de> Visitor<'de> for MyTypeVisitor {
            type Value = MyType;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(formatter, "struct or array of 4 integers")
            }

            fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
                // ...
            }
        }

        // deserializer.deserialize_any(MyTypeVisitor)
    }
}

Это возможно? В этом примере это несложно, но когда структура большая, десериализация от руки может быть болезненной.

Это не дубликат Как преобразовать поля во время десериализации с помощью Serde?, потому что deserialize_with работает только для 1 поля. Я не могу понять, как бы это работало для моего настоящего типа:

pub enum Component {
    String(StringComponent),
    Translation(TranslationComponent),
    Score(ScoreComponent),
    Selector(SelectorComponent),
}

pub struct StringComponent {
    #[serde(flatten)] pub base: Base,
    pub text: String,
}

pub struct Base {
    // ...
    extra: Option<Vec<Component>>,
    // ...
}

Что я хочу сделать:

  • Если при десериализации введено число, верните Component::String. Это можно сделать с _10 _ / _ 11 _ / _ 12_ и друзьями.
  • Если ввод является строкой, снова верните Component::String. Это можно сделать с помощью _14 _ / _ 15_.
  • Если входом является массив [..], десериализуйте его как обычно, но сделайте присвоение элементов в массиве [1 ..] дополнительным элементам массива [0]. Это можно сделать с помощью visit_seq.
  • Если ввод - это карта, позвольте serde derive обработать ее.

person wingerse    schedule 28.03.2018    source источник


Ответы (1)


В документации Serde есть пример, показывающий, как реализовать десериализацию из либо строка, либо структура. Это эквивалент вашего случая, только меньше.

Важная часть заключается в следующем:

fn visit_map<M>(self, visitor: M) -> Result<T, M::Error>
where
    M: MapAccess<'de>,
{
    Deserialize::deserialize(de::value::MapAccessDeserializer::new(visitor))
}

Это делегирует встроенную реализацию десериализации. Поскольку все остальные случаи являются индивидуальными, это должно подойти.

person Shepmaster    schedule 28.03.2018
comment
Спасибо ‹3. Хитрость заключалась в том, чтобы получить Deserialize как обычно для моего типа и создать тип-оболочку для дополнительной логики десериализации, которую нужно делегировать. - person wingerse; 29.03.2018