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

时间:2017-05-26 15:41:23

标签: json rust serde

我想使用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"的字段。值。 例如。对于Helium中的"color": null,字段。

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

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

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

3 个答案:

答案 0 :(得分:9)

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

解决此问题的一种方法是根据@ user25064的答案调整我们的应用程序规范以接受null

null | string

Playground with minimal example

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

#[serde(deserialize_with=...)]

Playground

答案 1 :(得分:3)

任何可以为null的字段都应该是<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/MyEditTextTheme"/> 类型,以便您可以处理null情况。像这样的东西?

Option

答案 2 :(得分:3)

基于 here 中的代码,当存在 null 时需要反序列化默认值。

// Omitting other derives, for brevity 
#[derive(Deserialize)]
struct Foo {
   #[serde(deserialize_with = "deserialize_null_default")]
   value: String, 
}

fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
    T: Default + Deserialize<'de>,
    D: Deserializer<'de>,
{
    let opt = Option::deserialize(deserializer)?;
    Ok(opt.unwrap_or_default())
}

playground link 带有完整示例。这也适用于 VecHashMap