我想将有线格式(如 JSON)反序列化为下面的结构,但我无法为相应的 rust 类型Data
编写 serde实现。Deserialize
{ "type": "TypeA", "value": { "id": "blah", "content": "0xa1b.." } }
Run Code Online (Sandbox Code Playgroud)
enum Content {
TypeA(Vec<u8>),
TypeB(BigInt),
}
struct Value {
id: String,
content: Content,
}
struct Data {
typ: String,
value: Value,
}
Run Code Online (Sandbox Code Playgroud)
困难在于选择正确的枚举值Content
,这是基于typ
值的。据我所知,serde 中的反序列化是无状态的,因此没有办法
typ
知道反序列化时的值是什么content
(即使反序列化顺序是有保证的)typ
解串器然后收集它。如何使用 serde 来实现这一点?
我看过
Deserialize
所有类型,并且我的数据模型很大现有的 SO 答案通常利用相关领域处于同一级别的事实。这里的情况并非如此:实际的数据模型很大、很深,而且字段“相距很远”
使用tagging更简单,但改变你的数据结构:
use serde::{Deserialize, Deserializer}; // 1.0.130
use serde_json; // 1.0.67
#[derive(Debug, Deserialize)]
#[serde(tag = "type", content = "value")]
enum Data {
TypeA(Value<String>),
TypeB(Value<u32>),
}
#[derive(Debug, Deserialize)]
struct Value<T> {
id: String,
content: T,
}
fn main() {
let input = r#"{"type": "TypeA", "value": { "id": "blah", "content": "0xa1b..."}}"#;
let data: Data = serde_json::from_str(input).unwrap();
println!("{:?}", data);
}
Run Code Online (Sandbox Code Playgroud)
另外,您可以使用一些中介编写自己的自定义反序列化器serde_json::Value
:
use serde::{Deserialize, Deserializer};// 1.0.130
use serde_json; // 1.0.67
#[derive(Debug)]
enum Content {
TypeA(String),
TypeB(String),
}
#[derive(Debug)]
struct Value {
id: String,
content: Content,
}
#[derive(Debug)]
struct Data {
typ: String,
value: Value,
}
impl<'de> Deserialize<'de> for Data {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json: serde_json::value::Value = serde_json::value::Value::deserialize(deserializer)?;
let typ = json.get("type").expect("type").as_str().unwrap();
let value = json.get("value").expect("value");
let id = value.get("id").expect("id").as_str().unwrap();
let content = value.get("content").expect("content").as_str().unwrap();
Ok(Data {
typ: typ.to_string(),
value: Value {
id: id.to_string(),
content: {
match typ {
"TypeA" => Content::TypeA(content.to_string()),
"TypeB" => Content::TypeB(content.to_string()),
_ => panic!("Invalid type, but this should be an error not a panic"),
}
}
}
})
}
}
fn main() {
let input = r#"{"type": "TypeA", "value": { "id": "blah", "content": "0xa1b..."}}"#;
let data: Data = serde_json::from_str(input).unwrap();
println!("{:?}", data);
}
Run Code Online (Sandbox Code Playgroud)
免责声明:我没有正确处理错误,您也可以将内容匹配提取到例如函数中。上面的代码只是为了说明主要思想。
有几种不同的方法可以解决这个问题,例如使用 custom impl Deserialize for Data
,然后反序列化为 a serde_json::Value
,然后在类型之间手动调整。
作为一个例子,请查看我过去写的这个答案。它不是一对一的解决方案,但它可能会Deserialize
根据您的需要提供一些手动实施的提示。
话虽如此。就我个人而言,我更喜欢尽量减少必须impl Deserialize
手动进行的操作,而是反序列化为另一种类型,并使用#[serde(from = "FromType")]
.
首先,type_: String
我建议我们引入 ,而不是enum ContentType
。
#[derive(Deserialize, Clone, Copy, Debug)]
enum ContentType {
TypeA,
TypeB,
TypeC,
TypeD,
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们考虑一下您介绍的类型。正如您提到的,我已经添加了一些额外的变体Content
,这些变体可能不同。
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum Content {
TypeA(Vec<u8>),
TypeB(Vec<u8>),
TypeC(String),
TypeD { foo: i32, bar: i32 },
}
#[derive(Deserialize, Debug)]
struct Value {
id: String,
content: Content,
}
#[derive(Deserialize, Debug)]
#[serde(try_from = "IntermediateData")]
struct Data {
#[serde(alias = "type")]
type_: ContentType,
value: Value,
}
Run Code Online (Sandbox Code Playgroud)
还没有什么疯狂的事情,也没有什么不同。所有的“魔法”都发生在IntermediateData
类型中,以及impl TryFrom
.
首先,我们来介绍 a check_type()
,它接受 aContentType
并根据 进行检查Content
。如果Content
变体与变体不匹配ContentType
,则进行转换。
简而言之,当使用#[serde(untagged)]
then 时,当 serde 尝试反序列化时,Content
它将始终返回它可以反序列化的第一个成功变体(如果有)。因此,如果它可以反序列化 a Vec<u8>
,那么它总是会产生Content::TypeA()
. 知道了这一点,那么在我们的check_type()
, if the ContentType
isTypeB
和 the Content
is TypeA
。然后我们只需将其更改为TypeB
.
impl Content {
// TODO: impl proper error type instead of `String`
fn check_type(self, type_: ContentType) -> Result<Self, String> {
match (type_, self) {
(ContentType::TypeA, content @ Self::TypeA(_)) => Ok(content),
(ContentType::TypeB, Self::TypeA(content)) => Ok(Self::TypeB(content)),
(ContentType::TypeC | ContentType::TypeD, content) => Ok(content),
(type_, content) => Err(format!(
"unexpected combination of {:?} and {:?}",
type_, content
)),
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们需要的是中间IntermediateData
以及TryFrom
调用.check_type()
Content
#[derive(Deserialize, Debug)]
struct IntermediateData {
#[serde(alias = "type")]
type_: ContentType,
value: Value,
}
impl TryFrom<IntermediateData> for Data {
// TODO: impl proper error type instead of `String`
type Error = String;
fn try_from(mut data: IntermediateData) -> Result<Self, Self::Error> {
data.value.content = data.value.content.check_type(data.type_)?;
Ok(Data {
type_: data.type_,
value: data.value,
})
}
}
Run Code Online (Sandbox Code Playgroud)
就这样。现在我们可以根据以下内容对其进行测试:
// serde = { version = "1", features = ["derive"] }
// serde_json = "1.0"
use std::convert::TryFrom;
use serde::Deserialize;
// ... all the previous code ...
fn main() {
let json = r#"{ "type": "TypeA", "value": { "id": "foo", "content": [0, 1, 2, 3] } }"#;
let data: Data = serde_json::from_str(json).unwrap();
println!("{:#?}", data);
let json = r#"{ "type": "TypeB", "value": { "id": "foo", "content": [0, 1, 2, 3] } }"#;
let data: Data = serde_json::from_str(json).unwrap();
println!("{:#?}", data);
let json = r#"{ "type": "TypeC", "value": { "id": "bar", "content": "foo" } }"#;
let data: Data = serde_json::from_str(json).unwrap();
println!("{:#?}", data);
let json = r#"{ "type": "TypeD", "value": { "id": "baz", "content": { "foo": 1, "bar": 2 } } }"#;
let data: Data = serde_json::from_str(json).unwrap();
println!("{:#?}", data);
}
Run Code Online (Sandbox Code Playgroud)
然后它会正确地生成带有、、和最后一个的Data
s 。Content::TypeA
Content::TypeB
Content::TypeC
Content::TypeD
最后。有问题 #939讨论添加#[serde(validate = "...")]
. 不过,它是 2017 年创建的,所以我不会屏息以待。
归档时间: |
|
查看次数: |
6780 次 |
最近记录: |