我想使用Serde从Bowserinator on github反序列化化学元素JSON文件。为此,我创建了一个具有所有所需字段的结构,并派生了所需的宏:

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
    name: String,
    appearance: String,
    atomic_mass: f64,
    boil: f64,
    category: String,
    #[serde(default)]
    color: String,
    density: f64,
    discovered_by: String,
    melt: f64,
    #[serde(default)]
    molar_heat: f64,
    named_by: String,
    number: String,
    period: u32,
    phase: String,
    source: String,
    spectral_img: String,
    summary: String,
    symbol: String,
    xpos: u32,
    ypos: u32,
}

在到达包含“null”值的字段之前,它可以正常工作。
例如。用于氦气中的"color": null,字段。

我收到的错误消息是此字段的{ code: Message("invalid type: unit value, expected a string"), line: 8, column: 17 }

我尝试了#[serde(default)]宏。但这仅在JSON文件中缺少该字段时有效,而在存在null值时则无效。

我喜欢对标准宏进行反序列化,而无需对Visitor Trait进行编程。我有想念的把戏吗?

最佳答案

发生反序列化错误是因为struct定义与传入的对象不兼容:color字段也可以是null以及字符串,但是为该字段指定String类型会强制您的程序始终期望字符串。这是默认行为,这是有道理的。请注意,在Rust中String(或其他容器,例如Box)不是“可为空”的。至于null值未触发默认值的情况,这正是Serde的工作方式:如果object字段不存在,则它将起作用,因为您已经添加了默认field属性。另一方面,值为null的“颜色”字段根本不等于没有任何字段。
解决此问题的一种方法是调整应用程序的规范以接受null | string,如@ user25064的答案所指定:

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
    color: Option<String>,
}
Playground with minimal example
另一种方法是为该字段编写我们自己的反序列化例程,该例程将接受null并将其转换为String类型的其他内容。这可以通过属性#[serde(deserialize_with=...)]完成。
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
    #[serde(deserialize_with="parse_color")]
    color: String,
}

fn parse_color<'de, D>(d: D) -> Result<String, D::Error> where D: Deserializer<'de> {
    Deserialize::deserialize(d)
        .map(|x: Option<_>| {
            x.unwrap_or("black".to_string())
        })
}
Playground
也可以看看:
  • How can I distinguish between a deserialized field that is missing and one that is null?
  • 10-04 23:23
    查看更多