我想将一个相当深的 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)
当然,可以实现自定义反序列化,但我想知道是否有默认的方法来实现这一点。
我认为这是一个有趣的问题/挑战,所以我编写了一个简单的 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)
该属性必须在派生之前放置Serialize,Deserialize否则它将把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)
| 归档时间: |
|
| 查看次数: |
1842 次 |
| 最近记录: |