Основываясь на моем вопросе ранее, теперь мне нужно десериализовать массив различных объектов. Существует существующий вопрос именно для этой ситуации, но он не для 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
?
invalid type: integer 1234, expected struct Raw
, что, вероятно, более полезно. - person Frxstrem   schedule 14.06.2019"data did not match any variant of untagged enum Task"
при выполнении только одной из задач. - person E-rich   schedule 14.06.2019DailyTask
) вместо перечисленияTask
. - person Frxstrem   schedule 14.06.2019#[serde(untagged)]
всегда выбирает первое совпадение. ЛюбойWeeklyTask
также будет соответствоватьDailyTask
, поэтому вы никогда не десериализуете его какWeeklyTask
так, как вы написали свой код. - person Frxstrem   schedule 14.06.2019WeeklyTask
иDailyTask
должно исправить это. Но ошибкаinvalid type: integer '1234'
не имеет для меня смысла, поскольку она должна работать так же, как в эта площадка. - person E-rich   schedule 14.06.2019