Десериализовать структуру, содержащую тег и вектор перечислений, с помощью serde

у меня такая установка

#[derive(Deserialize)]
struct GameData {
    game: String,
    player_data: Vec<MarioPartyData>,
    <more common data>
}

#[derive(Deserialize)]
#[serde(untagged)]
enum MarioPartyData {
    MarioParty(MarioParty),
    MarioParty2(MarioParty2),
    MarioParty3(MarioParty3),
    <many more variants>
}

#[derive(Deserialize)]
struct MarioParty {
    stars: i32,
    coins: i32,
    spaces_walked: i32
}

#[derive(Deserialize)]
struct MarioParty2 {
    stars: i32,
    coins: i32
}

#[derive(Deserialize)]
struct MarioParty3 {
    stars: i32,
    coins: i32
}

Это почти работает. Проблема в том, что некоторые игры имеют одинаковую структуру, поэтому, когда я десериализую MarioParty3, в данном случае он становится вариантом MarioParty2, потому что это первый вариант, которому он соответствует. Поле game должно однозначно определять, в какую игру нужно десериализовать, но я не могу понять, как использовать различные макросы тегов в serde для этого и возможно ли это вообще. Я надеюсь избежать реализации десериализатора самостоятельно. Также есть небольшая морщинка в том, что поле game будет не "MarioParty3", а скорее "Mario Party 3", хотя я думаю, что это можно было бы решить с помощью чего-то вроде #[serde(rename = "Mario Party 3")]?

Действительные входные данные JSON для экземпляра MarioParty3 могут выглядеть так:

{
  "game": "Mario Party 3",
  "player_data": [
    {
      "stars": 3,
      "coins" 45
    }
  ]
}

Я не против изменения каких-либо структур, которые у меня есть в rust, но вход JSON довольно устойчив. Любой совет приветствуется!

Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
1
0
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Самый простой способ представить этот JSON с помощью Serde — использовать перечисление со смежными тегами.

#[derive(Deserialize)]
struct GameData {
    #[serde(flatten)]
    player_data: MarioPartyData,
    // <more common data>
}

#[derive(Deserialize)]
#[serde(tag = "game", content = "player_data")]
enum MarioPartyData {
    #[serde(rename = "Mario Party")]
    MarioParty(Vec<MarioParty>),
    #[serde(rename = "Mario Party 2")]
    MarioParty2(Vec<MarioParty2>),
    #[serde(rename = "Mario Party 3")]
    MarioParty3(Vec<MarioParty3>),
    // <many more variants>
}

Это меняет структуру, так что Vec встречается внутри перечисления, заставляя каждый Vec содержать только один тип. Похоже, это хорошо отражает ваш JSON, но может изменить то, как вы с ним справляетесь в Rust.

Ах! Я не рассматривал возможность перемещения Vec внутри перечисления. Кажется, это работает! И на самом деле он лучше представляет данные: каждый экземпляр в Vec должен относиться к одному и тому же варианту. Для потомков мне не было очевидно, что мне нужно удалить поле game из GameData, чтобы это решение работало, но после этого это здорово! Спасибо!

ToxicGLaDOS 15.06.2024 05:43

Другие вопросы по теме