我想使用Serde 在github上反序列化来自Bowserinator的化学元素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,
}
Run Code Online (Sandbox Code Playgroud)
这工作正常,直到它到达包含"null"值的字段.例如,"color": null,在氦气场.
我得到的错误信息是{ code: Message("invalid type: unit value, expected a string"), line: 8, column: 17 }这个字段.
我试验了#[serde(default)]宏.但这仅在JSON文件中缺少字段时才有效,而不是在有null值时.
我喜欢使用标准宏进行反序列化,避免编写访问者特征.有没有我想念的技巧?
E_n*_*ate 13
由于结构定义与传入对象不兼容,因此发生反序列化错误:color字段也可以null是字符串,但是为该字段提供类型会String强制程序始终期望字符串.这是默认行为,这是有道理的.请注意,String(或其他容器,如Box)在Rust中不是"可空的".至于null不触发默认值的值,这就是Serde的工作方式:如果对象字段不在那里,它会起作用,因为你已经添加了默认的字段属性.另一方面,具有该值的字段"颜色" null不等于根本没有字段.
解决此问题的一种方法是null | string根据@ user25064的答案调整我们的应用程序规范以接受:
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
color: Option<String>,
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是为字段编写我们自己的反序列化例程,它将接受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())
})
}
Run Code Online (Sandbox Code Playgroud)
任何可以为 null 的字段都应该是一个Option类型,以便您可以处理 null 情况。像这样的东西吗?
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
...
color: Option<String>,
...
}
Run Code Online (Sandbox Code Playgroud)
基于此处的代码,当需要反序列化默认值(如果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())
}
Run Code Online (Sandbox Code Playgroud)
带有完整示例的游乐场链接。这也适用于Vec和HashMap。