在先前my question的基础上,我现在需要对一系列不同的对象进行反序列化。确实有一种existing question用于这种情况,但不适用于Serde。
use serde::{Deserialize, Deserializer};
use serde_json;
#[derive(Debug)]
struct Duration {
secs: u64,
nanos: u32,
}
#[derive(Deserialize)]
struct Raw<'a> {
#[serde(borrow)]
secs: StrOrNum<'a>,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum StrOrNum<'a> {
Str(&'a str),
Num(u64),
Decimal(f64),
}
#[derive(Debug, Deserialize)]
struct DailyTask {
name: String,
duration: Duration,
}
#[derive(Debug, Deserialize)]
struct WeeklyTask {
name: String,
duration: Duration,
priority: u8,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum Task {
Daily(DailyTask),
Weekly(WeeklyTask),
}
#[derive(Debug, Deserialize)]
struct Tasks {
tasks: Vec<Box<Task>>,
}
impl<'de> Deserialize<'de> for Duration {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let raw = Raw::deserialize(deserializer)?;
match raw.secs {
StrOrNum::Str(s) => {
if s.parse::<f64>().is_ok() {
let mut p = s.splitn(2, ".").fuse();
let secs = p.next().map_or(0, |s| s.parse().unwrap_or(0));
let frac = p.next().map_or(0, |s| s.parse().unwrap_or(0));
let nanos = frac.to_string().len() as u32;
let secs = secs * 10_u64.pow(nanos) + frac;
Ok(Duration { secs, nanos })
} else {
Err(serde::de::Error::custom(format!("Not a valid decimal: \"{}\"", s)))
}
}
StrOrNum::Num(secs) => Ok(Duration { secs, nanos: 0 }),
StrOrNum::Decimal(secs) => {
Ok(Duration { secs: secs as u64, nanos: 0 })
},
}
}
}
fn main() {
let json_raw = r#"{
"tasks": [
{
"name": "go to sleep",
"duration": 1234
},
{
"name": "go to work",
"duration": "98.7"
},
{
"name": "do that important thing",
"duration": 56.78,
"priority": 10
}
]
}"#;
let j: Tasks = serde_json::from_str(&json_raw).unwrap();
println!("{:?}", j);
}
我不确定自己出了什么问题。是否也有一个简单的解决方案,还是我需要以某种方式为Deserialize
实现自定义enum Task
?