serde json deserialize any number [duplicate]

心不动则不痛 提交于 2019-12-11 02:07:09

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!