问题
I am trying to combine the Either string or struct and the Manually deserialize struct examples by parsing something like:
{
"secs": "12.34"
}
Or
{
"secs": 5678
}
Into the struct:
struct Duration {
secs: u64,
nanos: u32,
}
Where both the secs
and nanos
members are set based on the "secs"
value only. Below is my non-working attempt at implementing custom deserialization this way, but I'm not sure how to delegate to the string_or_int
function for the "secs"
value.
use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;
use std::num::ParseIntError;
use serde::Deserialize;
use serde::de::{self, Deserializer, Visitor, MapAccess};
#[derive(Debug)]
struct Duration {
secs: u64,
nanos: u32,
}
impl<'de> Deserialize<'de> for Duration {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
enum Field { Secs };
impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
where
D: Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("`secs`")
}
fn visit_str<E>(self, value: &str) -> Result<Field, E>
where
E: de::Error,
{
match value {
"secs" => Ok(Field::Secs),
_ => Err(de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct DurationVisitor;
impl<'de> Visitor<'de> for DurationVisitor {
type Value = Duration;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Duration")
}
fn visit_map<V>(self, mut map: V) -> Result<Duration, V::Error>
where
V: MapAccess<'de>,
{
let mut secs = None;
let mut nanos = None;
while let Some(key) = map.next_key()? {
match key {
Field::Secs => {
// TODO somehow delegate to string_or_int and
// set secs and nanos based on secs value.
// if secs is int:
// secs = the int value
// nanos = 0
// if secs is string:
// secs = the int value * 10^(digit count right of '.')
// nanos = digit count right of '.'
// e.g. given value of 12.34
// secs = 1234
// nanos = 2
}
}
}
let secs = secs.ok_or_else(|| de::Error::missing_field("secs"))?;
let nanos = nanos.ok_or_else(|| de::Error::missing_field("nanos"))?;
Ok(Duration {
secs,
nanos,
})
}
}
const FIELDS: &'static [&'static str] = &["secs"];
deserializer.deserialize_struct("Duration", FIELDS, DurationVisitor)
}
}
fn string_or_int<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: Deserialize<'de> + FromStr<Err = ParseIntError>,
D: Deserializer<'de>,
{
struct StringOrInt<T>(PhantomData<fn() -> T>);
impl<'de, T> Visitor<'de> for StringOrInt<T>
where
T: Deserialize<'de> + FromStr<Err = ParseIntError>,
{
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or int")
}
fn visit_str<E>(self, value: &str) -> Result<T, E>
where
E: de::Error,
{
Ok(FromStr::from_str(value).unwrap())
}
}
deserializer.deserialize_any(StringOrInt(PhantomData))
}
fn main() {
let json_raw = r#"{ "secs": "1.000" }"#;
let j: Duration = serde_json::from_str(&json_raw).unwrap();
println!("{:?}", j);
}
How do I go about delegating the custom deserialization of a string or int for the "secs"
value and then set both the secs
and nanos
struct member values?
Thanks to @Shepmaster, the simplified solution is:
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),
}
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 }),
}
}
}
fn main() {
let json_raw = r#"{ "secs": 1234 }"#;
let j: Duration = serde_json::from_str(&json_raw).unwrap();
println!("{:?}", j);
let json_raw = r#"{ "secs": "12.34" }"#;
let j: Duration = serde_json::from_str(&json_raw).unwrap();
println!("{:?}", j);
let json_raw = r#"{ "secs": "12.3.4" }"#;
let j: Duration = serde_json::from_str(&json_raw).unwrap();
println!("{:?}", j);
}
来源:https://stackoverflow.com/questions/56582722/serde-json-deserialize-any-number