Как я могу десериализовать перечисление с необязательным внутренним тегом? - PullRequest
1 голос
/ 14 апреля 2020

Я использую Serde для десериализации пользовательского файла конфигурации, написанного на YAML. Файл может содержать определения различных видов, которые я представляю как внутренние теговые перечисления:

OfKindFoo:
  kind: Foo
  bar: bar;
  baz: baz;

OfKindQux:
  kind: Qux
  quux: qux;

Я представляю его в Rust следующим образом:

#[derive(Deserialize)]
#[serde(tag = "kind")]
enum Definition {
    Foo(Foo),
    Qux(Qux),
}

#[derive(Deserialize)]
struct Foo {
    bar: String,
    baz: String,
}

#[derive(Deserialize)]
struct Qux {
    quux: String,
}

Я хочу, чтобы пользователь мог Поля kind опущены полностью, и когда оно опущено, Serde должен по умолчанию десериализовать его как Foo.

. Я начал внедрять Deserialize в Definition. Я пытаюсь десериализовать его как карту и найти ключ kind и вернуть соответствующий вариант enum на основе этого ключа и его наличия.

Мне нужно как-то "переслать" десериализацию других полей карты на Foo::deserialize или Bar::deserialize, соответственно. fn deserialize принимает только один аргумент Deserializer. Есть ли способ «преобразовать» карту в десериализатор или иным образом получить десериализатор, который «запускается» на этой конкретной карте?

Я не могу использовать #[serde(other)], потому что он возвращает Err за отсутствующий тег. Даже если это не так, в документации говорится, что other может применяться только к «варианту устройства», варианту, не содержащему никаких данных.

1 Ответ

4 голосов
/ 15 апреля 2020

Вы можете пометить основное перечисление как untagged и добавить теги к подструктурам, у которых есть тег (эта функция не документирована , но была добавлена ​​намеренно и, вероятно, останется) , Вариант без тега должен быть объявлен после других, так как serde попытается десериализовать варианты в объявленном порядке с #[serde(untagged)]. Также обратите внимание, что если в вашем фактическом коде варианты и структуры имеют разные имена, или вы используете #[serde(rename)], то при этом имена структур имеют значение для (де) сериализации, а не имена вариантов. Все, что относится к вашему примеру:

#[derive(Deserialize)]
#[serde(untagged)]
enum Definition {
    Qux(Qux),
    Foo(Foo), // variant that doesn't have a tag is the last one
}

#[derive(Deserialize)]
struct Foo {
    bar: String,
    baz: String,
}

#[derive(Deserialize)]
#[serde(tag = "kind")]
// if you want the tag to be "qux" instead of "Qux", do
// #[serde(rename = "qux")]
// here (or add `, rename = "qux"` to the previous serde attribute)
struct Qux {
    quux: String,
}
...