初学rust,多态JSON对象的序列化与反序列化
Tag rust, 序列化, 反序列化, on by view 147

在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_typeresizecontent字段是一种结构体,msg_typepingcontent字段是另一种结构体。我们定义枚举和结构体如下

#[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,
}

可以看到我们定义了两种类型ResizeControlTimeControl,需要注意以下几点:

  1. 要支持反序列化,所有用到的结构体都需要添加Deserialize注解
  2. 枚举中,Empty,Resize,Time这3个都属于枚举的字段名,圆括号()里定义类型,没有定义的表示空
  3. 枚举上,需要添加untagged注解
  4. 要为枚举实现Default::default方法,否则无法被Deserialize注解
  5. 被枚举引用的结构体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