Rust Serde - 是否可以将可能位于两种不同布局中的 json 数据映射回单个结构?

Rust Serde - Is it possible to map json data the could be in two different layouts back to a single struct?

我试图映射到我的结构的数据有两种格式:

使用card_faces,当牌面超过一张时:

{
  "object": "card",
  "id": "some_id",
  "lang": "en",
  "released_at": "2012-02-03",
  "card_faces": [
    {
      "name": "some_name",
      "cost": "5",
      "ctype": "some_type",
      "colors": [
        "R",
        "B"
      ]
    },
    {
      "name": "another_name",
      "cost": "",
      "ctype": "another_type",
      "colors": [
        "R",
        "B"
      ]
    }
  ],
  "set_code": "some_code"
}

而没有card_faces,当只有一张人脸时(人脸字段只是放在根中):

{
  "object": "card",
  "id": "some_id",
  "lang": "en",
  "released_at": "2012-02-03",
  "name": "some_name",
  "cost": "5",
  "ctype": "some_type",
  "colors": [
    "R",
    "B"
  ],
  "set_code": "some_code"
}

我希望我的结构始终有一个 Vec<CardFace> 字段。类似于:

#[derive(Deserialize)]
struct Card {
  object: String,
  id: String,
  lang: String,
  released_at: String,
  faces: Vec<CardFace>,
  set_code: String,
}

是否可以反序列化这些对象并将面部字段强制转换为所需的格式,或者我是否需要在反序列化之前操作 json?

我会通过中介来做到这一点 enum:

#[derive(Deserialize)]
#[serde(untagged)]
enum CardTmpDeser {
    Card {
        object: String,
        id: String,
        lang: String,
        released_at: String,
        card_faces: Vec<CardFace>,
        set_code: String,
    },
    SingleCard {
        object: String,
        id: String,
        lang: String,
        released_at: String,
        name: String,
        cost: String,
        ctype: String,
        colors: Vec<String>,
        set_code: String,
    },
}

使用 #[serde(untagged)] 属性,您可以透明地反序列化您拥有的两种数据。

现在只需用 #[serde(from = "CardTmpDeser")] 标记您的实际结构:

#[derive(Debug, Deserialize)]
#[serde(from = "CardTmpDeser")]
pub struct Card {
    object: String,
    id: String,
    lang: String,
    released_at: String,
    card_faces: Vec<CardFace>,
    set_code: String,
}

并为 Card 实施 From<CardTmpDeser>,您就可以开始了! serde 将使用 CardTmpDeser 自动反序列化您的数据,但会透明地将其转换为您的最终类型。

(Permalink to the playground with full working example)