我正在尝试手动反序列化可以使用相同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
是否有更好的方法可以根据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)),
}
}
答案 0 :(得分:0)
我终于找到了一个可行的解决方案,但是我不确定这是否是反序列化多态JSON对象数组的惯用方式。
TL; DR 手动解析visit_map
中ParamVisitor
(即不是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");
}
}
}
答案 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);
}
另请参阅: