如何使用Serde反序列化包含空值的JSON文件?

我想使用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,在Helium中.

我得到的错误消息是{code:Message(“invalid type:unit value,expected a string”),line:8,column:17} for this field.

我试验了#[serde(默认)]宏.但这仅在JSON文件中缺少字段时才有效,而不是在存在空值时.

我喜欢使用标准宏进行反序列化,避免编写访问者特征.有没有我想念的技巧?

最佳答案
由于结构定义与传入对象不兼容,因此发生反序列化错误:颜色字段也可以为null,也可以是字符串,但为此字段提供String类型会强制程序始终期望字符串.这是默认行为,这是有道理的.请注意,String(或其他容器,如Box)在Rust中不是“可空的”.对于不触发默认值的空值,这就是Serde的工作原理:如果对象字段不存在,它将起作用,因为您添加了默认字段属性.另一方面,值为null的字段“color”不等于no字段.

解决此问题的一种方法是调整应用程序的规范以接受null |字符串,由@ 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

转载注明原文:如何使用Serde反序列化包含空值的JSON文件? - 代码日志