Я пытаюсь вручную десериализовать структуру, которая может использовать тот же атрибут 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");
}
}
}
детская площадка
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
Есть лилучший способ я могу вернуть другой результат Field
для ключа JSON на основе типа значения JSON?Я на правильном пути?
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)),
}
}