2017-05-26 17 views
2

我想使用Serde从Bowserinator on github反序列化化学元素JSON文件。为此,我创建了所有必要的字段的结构和衍生所需的宏:如何使用Serde反序列化包含空值的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, 
} 

这工作得很好,直到它到达它包含一个“空”值字段。 例如在氦气中的场地"color": null,

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

我试用了#[serde(default)]宏。但是这只适用于JSON文件中缺少字段的情况,而不是当存在null值时。

我喜欢用避免编程访问者特征的标准宏进行反序列化。有没有我想念的招数?

+1

这是**强烈推荐**你读[*锈病编程语言*](https://doc.rust-lang.org/stable/book/),其涵盖了Rust中非常普遍的'Option'和'Result'的概念。 – Shepmaster

+0

我已经这样做了,但提示将有助于如何处理这种情况,因为它似乎需要考虑与我预期的有点不同。正如我上面所说的,我的假设是我需要实现访问者特征,并且我想避免这种情况。正如我下面所说的:我还想避免再次解析所有读取的结构,并希望Serde具有某种帮助的魔力。 – Hartmut

+0

如果你提供了[MCVE],你的问题会更清楚。原来,你已经提供了代码和输入,但不是你想要的输出*。正如你所看到的,你提出的含糊不清的结果导致了两个截然不同的答案。 – Shepmaster

回答

6

反序列化错误,因为结构定义为与输入的对象不兼容:在color场也可以null,以及一个字符串,但给这个领域的类型String力量你的程序总是期待串。这是默认行为,这是有道理的。请注意,String(或其他容器,例如Box)在Rust中不是“空”。至于null值不会触发默认值,这就是Serde的工作方式:如果对象字段不存在,它将工作,因为您已添加默认字段属性。另一方面,具有值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

+0

谢谢,尤其是对于解释。我想我会采用第二种方式,所以我可以避免翻译课(从选项到我喜欢使用的结构) – Hartmut

2

任何可以为空的字段应该是Option类型,以便您可以处理空大小写。像这样?发生

#[derive(Serialize, Deserialize, Debug, Clone)] 
pub struct Element { 
    ... 
    color: Option<String>, 
    ... 
} 
+0

我希望有一些技巧可以自动执行此转换。我想避免在它从Serde解析器返回后第二次解析Element结构并自己修复所有的null值。 – Hartmut

+2

也许您在寻找[serde default](https://serde.rs/attr-default.html) – user25064