json rust serde -在反序列化时使路径变平

ioekq8ef  于 2023-04-13  发布在  其他
关注(0)|答案(1)|浏览(180)

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

{
  "root": {
    "f1": {
      "f2": {
         "f3": 123
       }
    }
  }
}

当派生Deserialize时,我将不得不创建太多的结构体-上面的JSON的每个级别一个:

struct Root {
  f1: Field1
}
struct Field1 {
  f2: Field2
}
struct Field3 {
  f3: Field3
}
// ...

有没有什么方法可以避免这个数量的结构体。我没有找到任何属性,这可能对派生有帮助。我想有这样的东西:

struct Root {
  // some attr?
  f3: u64
}

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

sczxawaw

sczxawaw1#

我认为这是一个有趣的问题/挑战,所以我写了一个简单的proc-macro属性来完成这个任务,称为serde_flat_path。下面是一个如何使用它来提供问题中描述的功能的示例:

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

该属性必须在派生SerializeDeserialize之前放置,因为它将serde属性放置在具有#[flat_path(...)]的字段上。我试图确保该属性尽可能与其他serde属性和helper crate一起使用。它也可以用于更复杂的类型,如下面的类型。对于SerializerDeserializer来说,它看起来应该和实际写出链中的所有结构体没有什么不同。

#[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,
    }
}

公平的警告,虽然这个proc-macro并不完美。目前它不能处理重叠的扁平化路径,由于宏的方式展开。如果尝试这样做,将发出编译时错误,除非你使用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}}}

相关问题