首页 > 解决方案 > 有没有办法在使用 Serde 反序列化对象时省略包装器/根对象?

问题描述

我有以下对象:

{
  "data": {
    "id": 1,
    "name": "South America",
    "countries": {
      "data": [
        {
          "id": 122,
          "name": "Brazil",
          "capital": "Brasilia"
        }
      ]
    }
  }
}

我想定义两个结构,Continent并且Country省略data不增加价值的包装。

标签: rustserde

解决方案


我将使用一个包装结构来实现这一点,该结构可以直接用于删除顶层嵌套,也可以通过一个#[serde(with = "...")]属性来消除反序列化数据结构中的嵌套级别。


use serde::{Deserialize, Deserializer};

#[derive(Deserialize, Debug)]
struct Continent {
    id: u64,
    name: String,
    #[serde(with = "Wrapper")]
    countries: Vec<Country>,
}

#[derive(Deserialize, Debug)]
struct Country {
    id: u64,
    name: String,
    capital: String,
}

#[derive(Deserialize)]
struct Wrapper<T> {
    data: T,
}

impl<T> Wrapper<T> {
    fn deserialize<'de, D>(deserializer: D) -> Result<T, D::Error>
    where
        T: Deserialize<'de>,
        D: Deserializer<'de>,
    {
        let wrapper = <Self as Deserialize>::deserialize(deserializer)?;
        Ok(wrapper.data)
    }
}

fn main() -> serde_json::Result<()> {
    let j = r#"
        {
          "data": {
            "id": 1,
            "name": "South America",
            "countries": {
              "data": [
                {
                  "id": 122,
                  "name": "Brazil",
                  "capital": "Brasilia"
                }
              ]
            }
          }
        }"#;

    let wrapper: Wrapper<Continent> = serde_json::from_str(j)?;
    println!("{:#?}", wrapper.data);

    Ok(())
}

在三个实质上不同的地方出现了无关紧要的嵌套:

  1. 与其他领域相邻
  2. 自己在顶层
  3. 本身低于顶层

这三个都需要不同的方法。在这个问题中观察到#2 和#3。

要解决 #1,请参阅Is it possible to flatten sub-object fields while parsing with serde_json?


推荐阅读