在rust中序列化反序列化JSON我们常用的是serde
这个包,但是json中有一种情况就是存在不确定类型的字段,或者说这个字段可能是多种类型;在golang中,我们用interface{}
类型来表达,在java中,我们用Object
表达,那么在rust应该如何表达呢。直接说结果,用enum来组合类型
比如我定义了一种JSON数据结构,如下
// 第一种
{
"msg_type": "resize",
"content": {
"width": 12,
"height": 3
}
}
// 第二种
{
"msg_type": "ping",
"content": {
"ts": 123
}
}
很明显,两个数据中,content字段表现的类型不一样,在msg_type
为resize
时content
字段是一种结构体,msg_type
为ping
时content
字段是另一种结构体。我们定义枚举和结构体如下
#[derive(Clone, Debug, Deserialize)]
struct ControlMessage {
msg_type: String,
#[serde(default)]
content: ControlContent,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum ControlContent {
Empty,
Resize(ResizeControl),
Time(TimeControl),
}
impl Default for ControlContent {
fn default() -> Self {
Self::Empty
}
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
struct ResizeControl {
width: u32,
height: u32,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
struct TimeControl {
ts: u32,
}
可以看到我们定义了两种类型ResizeControl
和TimeControl
,需要注意以下几点:
- 要支持反序列化,所有用到的结构体都需要添加
Deserialize
注解 - 枚举中,
Empty
,Resize
,Time
这3个都属于枚举的字段名,圆括号()里定义类型,没有定义的表示空 - 枚举上,需要添加
untagged
注解 - 要为枚举实现
Default::default
方法,否则无法被Deserialize
注解 - 被枚举引用的结构体
ResizeControl
,TimeControl
,以及枚举ControlContent
都需要加上PartialEq
注解,否则无法在枚举圆括号()中引用
调用反序列化
#[test]
pub fn test_field_deserialize() -> serde_json::Result<()> {
let a = r#"{"msg_type": "resize", "content": {"width": 12, "height": 3}}"#;
let v: ControlMessage = serde_json::from_str(a)?;
println!("v: {:?}", v);
let b = r#"{"msg_type": "resize", "content": {"ts": 123}}"#;
let v: ControlMessage = serde_json::from_str(b)?;
println!("v: {:?}", v);
Ok(())
}
结果调用成功
running 1 test
v: ControlMessage { msg_type: "resize", content: Resize(ResizeControl { width: 12, height: 3 }) }
v: ControlMessage { msg_type: "resize", content: Time(TimeControl { ts: 123 }) }
test control::test_field_deserialize ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s