rust serde - 反序列化时展平路径

Rah*_*hul 12 json rust serde

我想将一个相当深的 JSON 反序列化为 Rust 结构:

{
  "root": {
    "f1": {
      "f2": {
         "f3": 123
       }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

推导时Deserialize,我将不得不创建太多的结构 - 对于上述 JSON 的每个级别一个:

struct Root {
  f1: Field1
}
struct Field1 {
  f2: Field2
}
struct Field3 {
  f3: Field3
}
// ...
Run Code Online (Sandbox Code Playgroud)

有什么方法可以避免拥有这么多的结构。我没有找到任何对派生有帮助的属性。我想要这样的东西:

struct Root {
  // some attr?
  f3: u64
}
Run Code Online (Sandbox Code Playgroud)

当然,可以实现自定义反序列化,但我想知道是否有默认的方法来实现这一点。

Loc*_*cke 1

我认为这是一个有趣的问题/挑战,所以我编写了一个简单的 proc-macro 属性来执行此操作,称为serde_flat_path. 以下是如何使用它来提供问题中描述的功能的示例:

#[serde_flat_path::flat_path]
#[derive(Serialize, Deserialize)]
struct Root {
    #[flat_path(path = ["f1", "f2", "f3"])]
    f3: u64,
}
Run Code Online (Sandbox Code Playgroud)

该属性必须在派生之前放置SerializeDeserialize否则它将把serde属性放置在带有#[flat_path(...)]. 我试图确保此属性尽可能与其他 serde 属性和 helper crate 配合良好。它还可以用于更复杂的类型,如下所示。对于Serializeror来说Deserializer,它应该看起来与实际写出链中的所有结构没有什么不同。有关具体信息,您可以查看板条箱的自述文件。

#[serde_flat_path::flat_path]
#[derive(Serialize, Deserialize)]
#[serde(tag = "foobar")]
pub enum Bar {
    Foo {
        #[flat_path(path = ["a", "b", "c", "d", "e"])]
        #[serde(with = "flip_bool")]
        foo: bool,
        #[flat_path(path = ["x", "y", "z"])]
        #[serde(skip_serializing_if = "Option::is_some")]
        x: Option<u64>,
    },
    Bar {
        #[flat_path(path = ["a", "b"])]
        bar: Bar,
    },
    Baz,
    Biz {
        #[serde(with = "add_one")]
        z: f64,
    }
}
Run Code Online (Sandbox Code Playgroud)

不过,这是公平的警告,因为这个过程宏并不完美。目前,由于宏的扩展方式,它无法处理重叠的扁平路径。如果尝试这样做,除非您使用该功能,否则将发出编译时错误allow_overlap。在某些情况下,它也与泛型作斗争,但我正在寻求改进这一点。

// This would produce an error since x and y have overlapping paths
#[serde_flat_path::flat_path]
#[derive(Serialize, Deserialize)]
struct Foo {
    z: bool,
    #[flat_path(path = ["a", "b", "x"])]
    x: u64,
    #[flat_path(path = ["a", "c", "y"])]
    y: u64,
}

let foo = Foo { z: true, x: 123, y: 456 };
println!("{}", serde_json::to_string(&foo).unwrap());
// Output:
// {"z":true,"a":{"b":{"x":123}},"a":{"c":{"y":456}}}
Run Code Online (Sandbox Code Playgroud)