【问题标题】:How to deserialize a JSON array into a struct using serde?如何使用 serde 将 JSON 数组反序列化为结构?
【发布时间】:2020-12-25 16:10:35
【问题描述】:

我正在尝试将以下 JSON sn-ps 反序列化为结构 ShapeVec

use serde::{Deserialize, Serialize};
use serde_json::{Result, Value};

#[derive(Debug, Serialize, Deserialize)]
struct Shape {  // this struct is not working, for display purpose only
    shape_type: String,
    d0: f64,
    d1: f64,
    d2: f64, //optional, like the case of "dot"
    d3: f64, //optional, like the case of "circle"
}

let json = r#"
  {[
    ["line", 1.0, 1.0, 2.0, 2.0],
    ["circle", 3.0, 3.0, 1.0],
    ["dot", 4.0, 4.0]
  ]}"#;

let data: Vec<Shape> = match serde_json::from_str(json)?;

显然,每种类型的Shape 都需要一个String 和不同数量的f64 来描述它。我应该如何定义Shape的结构来反序列化上面的JSON数据?

【问题讨论】:

  • 您的 JSON 数据不是有效的 JSON。对象内部不能有匿名数组。另外,是否可以修改 JSON 格式,还是必须以当前形式反序列化?
  • 我的错字。应该是 let json = r#"{"shapes": [["line", 1.0, 1.0, 2.0, 2.0], ..."#; 不幸的是我无法更改 JSON 格式。我尝试将 d2 & d3 的类型更改为 Option,但没有运气:"invalid length 4, expected struct Shape with 5 elements" at line of "circle".

标签: json struct rust deserialization serde


【解决方案1】:

假设您可以控制 JSON 格式,我强烈建议您将 Shape 类型转换为可以表示多个形状的 enum,并使用 serde 的派生宏来自动实现 SerializeDeserializeShape。示例:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Point {
    x: f64,
    y: f64,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
enum Shape {
    Dot { position: Point },
    Line { start: Point, end: Point },
    Circle { center: Point, radius: f64 },
}

fn main() {
    let shapes = vec![
        Shape::Dot {
            position: Point { x: 3.0, y: 4.0 },
        },
        Shape::Line {
            start: Point { x: -2.0, y: 1.0 },
            end: Point { x: 5.0, y: -3.0 },
        },
        Shape::Circle {
            center: Point { x: 0.0, y: 0.0 },
            radius: 7.0,
        },
    ];

    let serialized = serde_json::to_string(&shapes).unwrap();
    println!("serialized = {}", serialized);

    let deserialized: Vec<Shape> = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
}

playground

如果您绝对无法更改 JSON 格式,那么 serde 无法帮助您。将形状序列化为字符串和浮点数的异构数组是一个非常奇怪的选择。您必须自己手动解析它(或至少使用一些解析器板条箱来帮助您),然后 manually implement the Deserializer trait 将其转换为 Shape

【讨论】:

  • 好帖子。我会弄清楚是否可以更改 JSON 的格式。只是好奇,这段 JSON 可以很容易地用 Python json.loads(text) 解析。 Python 的列表可以包含不同类型的数据和可变数量的项目。 rust中有没有类似于Python的list的数据类型?
【解决方案2】:

我应该如何定义Shape的结构来反序列化上面的JSON数据?

你不会,因为你想要的序列化方案生锈并没有真正意义,而且 AFAIK serde 不支持它(即使你使用元组变体的enumtag="type" 也不支持支持)。

如果您真的不能或不想使用另一个答案中描述的更简单的结构和序列化方案,我能看到的唯一选择是实现自定义(反)序列化方案。

特别是因为每个“类型”的数量都发生了变化,否则 https://crates.io/crates/serde_tuple 会起作用(尽管您总是可以看到 skip_serializing_if 是否与 serde_tuple 一起使用,这会让您抑制“额外”字段)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-20
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2022-11-29
    • 1970-01-01
    • 2012-12-15
    相关资源
    最近更新 更多