相关文章推荐
含蓄的自行车  ·  android ...·  4 天前    · 
爱跑步的感冒药  ·  ubuntu ...·  6 月前    · 

RUST:多态结构JSON对象的反序列化

嗯,Rust初学者,在各种坑中摔的鼻青脸肿。

一个简单的“服务端JSON转为CSV”的小程序,如果是常用的Java、Ruby,甚至是Shell,一个下午的时间也差不多了,偏偏脑子一抽用了Rust,硬是折腾了快10天了。即使这样也不敢保证程序的强壮性。

这里记录一下,算是一篇Rust的学习笔记吧。

先描述一下我要解决的问题:

服务端返回的JSON结构有多种形态:

[
        "id": 1,
        "name": "..."
        "id": 2,
        "name": "...",
        "value": "..."
]

很简单的,一个结构体搞定:

#[derive(Clone, Debug, serde::Deserialize)]
struct CustomValue {
    id: u32,
    name: String,
    value: Option<String>,

借助 serde ,开发过程中这一步很容易就实现了,可发布之后却立马打脸。原来这个结构还有第三个形态。

{
    "id": 3,
    "name": "...",
    "multiple": true,
    "value": ["...", "..."]
}

中间试错的过程就不说了,直接给出解决方案: 用枚举替代结构体

#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
enum CustomValue {
    Multiple {
        id: u32,
        name: String,
        value: Vec<String>,
    Single {
        id: u32,
        name: String,
        value: String,
    Empty {
        id: u32,
        name: String,
#[test]
pub fn test_field_deserialize() -> serde_json::Result<()> {
    let a = r#"{"id": 1, "name": "empty"}"#;
    let b = r#"{"id": 2, "name": "single", "value": "string"}"#;
    let c = r#"{"id": 3, "name": "multiple", "multiple": true, "value": ["first", "second"]}"#;
    let mut v: CustomValue = serde_json::from_str(a)?;
    match v {
        CustomValue::Empty { id, name } => {
            assert_eq!(id, 1);
            assert_eq!(name, "empty");
        _ => {
            panic!("expect Empty");
    v = serde_json::from_str(b)?;
    match v {
        CustomValue::Single { id, name, value } => {
            assert_eq!(id, 2);
            assert_eq!(name, "single");
            assert_eq!(value, "string")
        _ => {
            panic!("expect Single");
    v = serde_json::from_str(c)?;
    match v {
        CustomValue::Multiple { id, name, value } => {
            let mut data = Vec::<String>::new();
            data.push("first".to_string());
            data.push("second".to_string());
            assert_eq!(id, 3);
            assert_eq!(name, "multiple");
            assert_eq!(value, data);
        _ => {
            panic!("expect Multiple");
    Ok(())

重点是:

  1. 使用 untagged 给枚举做注解
  2. 枚举值的顺序要注意。 serde 的做法是依次偿试,遇到的第一个能匹配的就做为结果返回。如果最先定义 Empty ,那么所有的形态都能与其匹配,那么就达不到我们的本意了。

说起来也是可恨,这个问题的对策,在 serde 的官方文档中就有示范( Enum representations )。可恨是的公司代理服务器一到用时就掉链子,访问外部网站简直想让人砸电脑。如果你让我看一下这个网页,十几分钟就能解决掉的问题,硬是花了我一天的时间没解决,最后是回到家里查看了官方文档后才恍然大悟。

好吧,我承认,这篇文章,与其说分享经验,不如说是吐槽公司IT环境。


强迫症犯了, id name 重复出现,不能忍。

进阶版如下:

#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum ValueType {
    Empty,
    String(String),
    Array(Vec<String>),
impl Default for ValueType {
    fn default() -> Self {
        Self::Empty
#[derive(Clone, Debug, Deserialize)]
struct Composite {
    id:u32,
    name:String,
    #[serde(default)]
    value: ValueType,
#[test]
pub fn test_composite_deserialize() -> serde_json::Result<()> {
    let a = r#"{"id": 1, "name": "empty"}"#;
    let b = r#"{"id": 2, "name": "single", "value": "string"}"#;
    let c = r#"{"id": 3, "name": "multiple", "multiple": true, "value": ["first", "second"]}"#;
    let mut v: Composite = serde_json::from_str(a)?;
    assert_eq!(v.id, 1);
    assert_eq!(v.name, "empty");
    assert_eq!(v.value, ValueType::Empty);
    v = serde_json::from_str(b)?;
    assert_eq!(v.id, 2);
    assert_eq!(v.name, "single");
    assert_eq!(v.value, ValueType::String("string".to_owned()));
    let mut data = Vec::<String>::new();
    data.push("first".to_string());
    data.push("second".to_string());
    v = serde_json::from_str(c)?;
    assert_eq!(v.id, 3);