实现JSON的serde :: Deserialize时无法捕获fn项中的动态环境

时间:2019-06-14 18:11:06

标签: rust deserialization json-deserialization serde

我正在尝试手动反序列化可以使用相同JSON属性作为不同JSON类型(例如,对象或字符串)的结构。例如:

[
  {
    "Name": "a single unit param",
    "Units": "m/s"
  },
  {
    "Name": "a multi unit param",
    "Units": {
      "Metric": {
        "Units": "m/s"
      },
      "Imperial": {
        "Units": "ft/s"
      }
    }
  }
]

我到目前为止所拥有的如下。我在Rust中没有足够的经验,无法弄清我要做什么。

use serde::de::{self, MapAccess, Visitor};
use serde::{Deserialize, Deserializer}; // 1.0.91
use std::fmt;

#[derive(Debug, Deserialize)]
pub struct SingleUnitParam {
    name: String,
    units: String,
}

#[derive(Debug, Deserialize)]
pub struct UnitInfo {
    units: String,
}

#[derive(Debug, Deserialize)]
pub struct MultiUnits {
    metric: UnitInfo,
    imperial: UnitInfo,
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum StrOrUnitsObj<'a> {
    Str(&'a str),
    UnitsObj(MultiUnits),
}

#[derive(Debug, Deserialize)]
pub struct MultiUnitParam {
    name: String,
    units: MultiUnits,
}

#[derive(Debug)]
pub enum Param {
    Single(SingleUnitParam),
    Multiple(MultiUnitParam),
}

impl<'de> Deserialize<'de> for Param {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        enum Field {
            Name,
            UnitsAsObj,
            UnitsAsStr,
        };

        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("`Name` or `Units`")
                    }

                    fn visit_str<E>(self, value: &str) -> Result<Field, E>
                    where
                        E: de::Error,
                    {
                        match value {
                            "Name" => Ok(Field::Name),
                            "Units" => Ok({
                                let val = StrOrUnitsObj::deserialize(deserializer).unwrap();

                                match val {
                                    StrOrUnitsObj::Str(s) => Field::UnitsAsObj,
                                    StrOrUnitsObj::UnitsObj(obj) => Field::UnitsAsStr,
                                }
                            }),
                            _ => Err(de::Error::unknown_field(value, FIELDS)),
                        }
                    }
                }

                deserializer.deserialize_identifier(FieldVisitor)
            }
        }

        struct ParamVisitor;

        impl<'de> Visitor<'de> for ParamVisitor {
            type Value = Param;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("enum Param")
            }

            fn visit_map<V>(self, mut map: V) -> Result<Param, V::Error>
            where
                V: MapAccess<'de>,
            {
                let mut name = None;
                let mut units_as_string = None;
                let mut units_as_object = None;
                while let Some(key) = map.next_key()? {
                    match key {
                        Field::Name => {
                            if name.is_some() {
                                return Err(de::Error::duplicate_field("Name"));
                            }
                            name = Some(map.next_value()?);
                        }
                        Field::UnitsAsObj => {
                            if units_as_object.is_some() {
                                return Err(de::Error::duplicate_field("Units"));
                            }
                            units_as_object = Some(map.next_value()?);
                        }
                        Field::UnitsAsStr => {
                            if units_as_string.is_some() {
                                return Err(de::Error::duplicate_field("Units"));
                            }
                            units_as_string = Some(map.next_value()?);
                        }
                    }
                }
                let name = name.ok_or_else(|| de::Error::missing_field("Name"))?;
                if let Some(units_as_object) = units_as_object {
                    Ok(Param::Multiple(MultiUnitParam {
                        name: name,
                        units: units_as_object,
                    }))
                } else {
                    let units_as_string =
                        units_as_string.ok_or_else(|| de::Error::missing_field("Units"))?;
                    Ok(Param::Single(SingleUnitParam {
                        name: name,
                        units: units_as_string,
                    }))
                }
            }
        }

        const FIELDS: &'static [&'static str] = &["Name", "Units"];
        deserializer.deserialize_struct("Param", FIELDS, ParamVisitor)
    }
}

fn main() {
    let json_raw = r#"[
            { "Name": "a single unit param", "Units": "m/s" },
            { "Name": "a multi unit param", "Units": { "Metric": { "Units": "m/s" }, "Imperial": { "Units": "ft/s" } } }
        ]"#;
    let j: Vec<Param> = serde_json::from_str(&json_raw).unwrap();
    match &j[0] {
        Param::Single(p) => {
            assert_eq!(p.name, "a single unit param");
            assert_eq!(p.units, "m/s");
        }
        Param::Multiple(_p) => panic!("Expected SingleUnitParam, actual MultiUnitParam"),
    }
    match &j[1] {
        Param::Single(_p) => panic!("Expected MultiUnitParam, actual SingleUnitParam"),
        Param::Multiple(p) => {
            assert_eq!(p.name, "a multi unit param");
            assert_eq!(p.units.metric.units, "m/s");
            assert_eq!(p.units.imperial.units, "ft/s");
        }
    }
}

playground

error[E0434]: can't capture dynamic environment in a fn item
  --> src/main.rs:74:70
   |
74 |                                 let val = StrOrUnitsObj::deserialize(deserializer).unwrap();
   |                                                                      ^^^^^^^^^^^^
   |
   = help: use the `|| { ... }` closure form instead

是否有更好的方法可以根据JSON值类型为JSON密钥返回不同的Field结果?我在正确的轨道上吗?

fn visit_str<E>(self, value: &str) -> Result<Field, E>
where
    E: de::Error,
{
    match value {
        "Name" => Ok(Field::Name),
        "Units" => Ok({
            let val = StrOrUnitsObj::deserialize(deserializer).unwrap();

            match val {
                StrOrUnitsObj::Str(s) => {
                    Field::UnitsAsObj
                },
                StrOrUnitsObj::UnitsObj(obj) => {
                    Field::UnitsAsStr
                }
            }
        }),
        _ => Err(de::Error::unknown_field(value, FIELDS)),
    }
}

2 个答案:

答案 0 :(得分:0)

我终于找到了一个可行的解决方案,但是我不确定这是否是反序列化多态JSON对象数组的惯用方式。

TL; DR 手动解析visit_mapParamVisitor(即不是FieldVisitor)中的JSON对象,以便我们可以检查已解析的JSON的类型,并根据类型设置相应的变量。

use std::fmt;
use serde::{Deserialize, Deserializer}; // 1.0.91
use serde::de::{self, Visitor, MapAccess};

#[derive(Debug, Deserialize)]
pub struct SingleUnitParam {
    name: String,
    units: String,
}

#[derive(Debug, Deserialize)]
pub struct UnitInfo {
    #[serde(alias = "Units")]
    units: String,
}

#[derive(Debug, Deserialize)]
pub struct MultiUnits {
    #[serde(alias = "Metric")]
    metric: UnitInfo,
    #[serde(alias = "Imperial")]
    imperial: UnitInfo,
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum StrOrUnitsObj<'a> {
    Str(&'a str),
    UnitsObj(MultiUnits)
}

#[derive(Debug, Deserialize)]
pub struct MultiUnitParam {
    name: String,
    units: MultiUnits,
}

#[derive(Debug)]
pub enum Param {
    Single(SingleUnitParam),
    Multiple(MultiUnitParam),
}

impl<'de> Deserialize<'de> for Param {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        enum Field { Name, Units/*, UnitsAsObj, UnitsAsStr*/ };

        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("`Name` or `Units`")
                    }

                    fn visit_str<E>(self, value: &str) -> Result<Field, E>
                    where
                        E: de::Error,
                    {
                        match value {
                            "Name" => Ok(Field::Name),
                            "Units" => Ok(Field::Units),
                            // Can't get access to the JSON value to inspect it here.
                            // "Units" => Ok({
                            //     let val = StrOrUnitsObj::deserialize(deserializer).unwrap();

                            //     match val {
                            //         StrOrUnitsObj::Str(s) => {
                            //             Field::UnitsAsObj
                            //         },
                            //         StrOrUnitsObj::UnitsObj(obj) => {
                            //             Field::UnitsAsStr
                            //         }
                            //     }
                            // }),
                            _ => Err(de::Error::unknown_field(value, FIELDS)),
                        }
                    }
                }

                deserializer.deserialize_identifier(FieldVisitor)
            }
        }

        struct ParamVisitor;

        impl<'de> Visitor<'de> for ParamVisitor {
            type Value = Param;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("enum Param")
            }

            fn visit_map<V>(self, mut map: V) -> Result<Param, V::Error>
            where
                V: MapAccess<'de>,
            {
                let mut name = None;
                let mut units_as_string = None;
                let mut units_as_object = None;
                while let Some(key) = map.next_key()? {
                    match key {
                        Field::Name => {
                            if name.is_some() {
                                return Err(de::Error::duplicate_field("Name"));
                            }
                            name = Some(map.next_value()?);
                        }
                        Field::Units => {
                            if units_as_string.is_some() || units_as_object.is_some() {
                                return Err(de::Error::duplicate_field("Units"));
                            }
                            // Here is where we can get the JSON value and check its type.
                            let v: serde_json::Value = map.next_value()?;
                            if v.is_object() {
                                let v: MultiUnits = serde_json::from_value(v).unwrap();
                                units_as_object = Some(v);
                            } else if v.is_string() {
                                units_as_string = Some(v.as_str().unwrap().to_owned());
                            }
                        }
                        // Field::UnitsAsObj => {
                        //     if units_as_object.is_some() {
                        //         return Err(de::Error::duplicate_field("Units"));
                        //     }
                        //     units_as_object = Some(map.next_value()?);
                        // }
                        // Field::UnitsAsStr => {
                        //     if units_as_string.is_some() {
                        //         return Err(de::Error::duplicate_field("Units"));
                        //     }
                        //     units_as_string = Some(map.next_value()?);
                        // }
                    }
                }
                let name = name.ok_or_else(|| de::Error::missing_field("Name"))?;
                if let Some(units_as_object) = units_as_object {
                    Ok(Param::Multiple(MultiUnitParam {
                        name: name,
                        units: units_as_object
                    }))
                } else {
                    let units_as_string = units_as_string.ok_or_else(|| de::Error::missing_field("Units"))?;
                    Ok(Param::Single(SingleUnitParam {
                        name: name,
                        units: units_as_string
                    }))
                }
            }
        }

        const FIELDS: &'static [&'static str] = &["Name", "Units"];
        deserializer.deserialize_struct("Param", FIELDS, ParamVisitor)
    }
}

fn main() {
    let json_raw = r#"[
        { "Name": "a single unit param", "Units": "m/s" },
        { "Name": "a multi unit param", "Units": { "Metric": { "Units": "m/s" }, "Imperial": { "Units": "ft/s" } } }
    ]"#;
    let j: Vec<Param> = serde_json::from_str(&json_raw).unwrap();
    match &j[0] {
        Param::Single(p) => {
            assert_eq!(p.name, "a single unit param");
            assert_eq!(p.units, "m/s");
        },
        Param::Multiple(_p) => panic!("Expected SingleUnitParam, actual MultiUnitParam")
    }
    match &j[1] {
        Param::Single(_p) => panic!("Expected MultiUnitParam, actual SingleUnitParam"),
        Param::Multiple(p) => {
            assert_eq!(p.name, "a multi unit param");
            assert_eq!(p.units.metric.units, "m/s");
            assert_eq!(p.units.imperial.units, "ft/s");
        }
    }
}

playground

答案 1 :(得分:0)

出现问题是因为您试图在内部函数中使用外部函数中的变量:

fn outer(id: i32) {
    fn inner() {
        println!("{}", id);
    }
}

您根本无法做到这一点:

error[E0434]: can't capture dynamic environment in a fn item
 --> src/lib.rs:3:24
  |
3 |         println!("{}", id);
  |                        ^^
  |
  = help: use the `|| { ... }` closure form instead

另请参阅:


您根本不需要编写任何自定义反序列化;使用Serde的属性足够强大:

use serde::Deserialize; // 1.0.91
use serde_json; // 1.0.39

#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Param<'a> {
    name: &'a str,
    #[serde(borrow)]
    units: Units<'a>,
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum Units<'a> {
    Str(&'a str),
    #[serde(borrow)]
    Multi(Multi<'a>),
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Multi<'a> {
    #[serde(borrow)]
    metric: SingleUnit<'a>,
    #[serde(borrow)]
    imperial: SingleUnit<'a>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct SingleUnit<'a> {
    units: &'a str,
}

fn main() {
    let json_text = r#"[
      {
        "Name": "a single unit param",
        "Units": "m/s"
      },
      {
        "Name": "a multi unit param",
        "Units": {
          "Metric": {
            "Units": "m/s"
          },
          "Imperial": {
            "Units": "ft/s"
          }
        }
      }
    ]"#;

    let x: Vec<Param<'_>> = serde_json::from_str(json_text).expect("Bad schema");

    println!("{:?}", x);
}

另请参阅: