如何使用 Serde 反序列化包含空值的 JSON 文件?
How to deserialize a JSON file which contains null values using 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
值时有效。
我喜欢使用标准宏进行反序列化,避免编写访问者特征。有没有我想念的把戏?
任何可以为 null 的字段都应该是 Option
类型,以便您可以处理 null 情况。是这样的吗?
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
...
color: Option<String>,
...
}
出现反序列化错误,因为结构定义与传入对象不兼容: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())
})
}
另请参阅:
基于 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 有完整的例子。这也适用于 Vec
和 HashMap
.
我想使用 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
值时有效。
我喜欢使用标准宏进行反序列化,避免编写访问者特征。有没有我想念的把戏?
任何可以为 null 的字段都应该是 Option
类型,以便您可以处理 null 情况。是这样的吗?
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
...
color: Option<String>,
...
}
出现反序列化错误,因为结构定义与传入对象不兼容: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())
})
}
另请参阅:
基于 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 有完整的例子。这也适用于 Vec
和 HashMap
.