在 Serde 中处理混合对象数组
Handling mixed object arrays in Serde
扩展我的 , how do you handle an array that contains mixed structs
that are both valid? I've tried looking at the serde_json::Value
来源。但是它不处理两个不同 structs
.
的情况
我不能简单地合并它们,并在它们的属性上使用 Options,因为这会使单个 struct
变得笨拙,并且它们是不同的很重要。
Rust 结构
#[derive(Clone, Debug, Deserialize)]
struct WebResponse {
foo: Vec<Structs>,
}
enum Structs {
Foo(Foo),
Bar(Bar),
}
#[derive(Clone, Debug, Deserialize)]
struct Foo {
name: String,
baz: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
struct Bar {
quux: u64
}
示例JSON
{
"foo": [
{
"name": "John",
"baz": ["Lorem", "Ipsum"]
},
{
"quux": 17
}
]
}
有几种方法可以解决这个问题。如果您的变体很少,最简单的方法就是像这样手动实现 Deserialize
:
impl serde::de::Deserialize for Structs {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: serde::Deserializer,
{
deserializer.deserialize(Visitor)
}
}
struct Visitor;
impl serde::de::Visitor for Visitor {
type Value = Structs;
fn visit_map<V>(&mut self, mut visitor: V) -> Result<Structs, V::Error>
where V: serde::de::MapVisitor,
{
let s: String = try!(visitor.visit_key()).expect("got struct with no fields");
let val = match &s as &str {
"name" => {
Ok(Structs::Foo(Foo {
name: try!(visitor.visit_value()),
baz: {
let s: String = try!(visitor.visit_key()).expect("baz field");
assert_eq!(&s, "baz");
try!(visitor.visit_value())
},
}))
},
"baz" => {
Ok(Structs::Foo(Foo {
baz: try!(visitor.visit_value()),
name: {
let s: String = try!(visitor.visit_key()).expect("name field");
assert_eq!(&s, "name");
try!(visitor.visit_value())
},
}))
},
"quux" => {
Ok(Structs::Bar(Bar {
quux: try!(visitor.visit_value())
}))
},
other => panic!("no struct has field `{}`", other),
};
try!(visitor.end());
val
}
}
此实现的问题在于它显然无法扩展。相反,您可以做的是创建一个新的 Deserializer
,您给找到的第一个字段名称并覆盖 deserialize_map
方法以通过自定义 MapVisitor
处理各种结构。
如果您认为这是其他序列化框架支持的常见情况,请随时 post serde
存储库或 serde-json
存储库中的错误报告。我确信有一种方法可以自动生成这样的实现,但它肯定不是微不足道的。
扩展我的 structs
that are both valid? I've tried looking at the serde_json::Value
来源。但是它不处理两个不同 structs
.
我不能简单地合并它们,并在它们的属性上使用 Options,因为这会使单个 struct
变得笨拙,并且它们是不同的很重要。
Rust 结构
#[derive(Clone, Debug, Deserialize)]
struct WebResponse {
foo: Vec<Structs>,
}
enum Structs {
Foo(Foo),
Bar(Bar),
}
#[derive(Clone, Debug, Deserialize)]
struct Foo {
name: String,
baz: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
struct Bar {
quux: u64
}
示例JSON
{
"foo": [
{
"name": "John",
"baz": ["Lorem", "Ipsum"]
},
{
"quux": 17
}
]
}
有几种方法可以解决这个问题。如果您的变体很少,最简单的方法就是像这样手动实现 Deserialize
:
impl serde::de::Deserialize for Structs {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: serde::Deserializer,
{
deserializer.deserialize(Visitor)
}
}
struct Visitor;
impl serde::de::Visitor for Visitor {
type Value = Structs;
fn visit_map<V>(&mut self, mut visitor: V) -> Result<Structs, V::Error>
where V: serde::de::MapVisitor,
{
let s: String = try!(visitor.visit_key()).expect("got struct with no fields");
let val = match &s as &str {
"name" => {
Ok(Structs::Foo(Foo {
name: try!(visitor.visit_value()),
baz: {
let s: String = try!(visitor.visit_key()).expect("baz field");
assert_eq!(&s, "baz");
try!(visitor.visit_value())
},
}))
},
"baz" => {
Ok(Structs::Foo(Foo {
baz: try!(visitor.visit_value()),
name: {
let s: String = try!(visitor.visit_key()).expect("name field");
assert_eq!(&s, "name");
try!(visitor.visit_value())
},
}))
},
"quux" => {
Ok(Structs::Bar(Bar {
quux: try!(visitor.visit_value())
}))
},
other => panic!("no struct has field `{}`", other),
};
try!(visitor.end());
val
}
}
此实现的问题在于它显然无法扩展。相反,您可以做的是创建一个新的 Deserializer
,您给找到的第一个字段名称并覆盖 deserialize_map
方法以通过自定义 MapVisitor
处理各种结构。
如果您认为这是其他序列化框架支持的常见情况,请随时 post serde
存储库或 serde-json
存储库中的错误报告。我确信有一种方法可以自动生成这样的实现,但它肯定不是微不足道的。