Search code examples
jsonserializationrustflattenserde

Rust serde parse Vec of substruct


Is it possible to just extract the Message struct of this json items array?
My goal is to get a Vec of Message(s) if there are multiple entries in items.

{
  "items": [
    {
      "Message": {
        "data": "data",
        "header": "header"
      },
      "structx": {
        "val": 1
      },
      "val1": "test"
    }
  ]
}

As of now i have those structs to deserialize the above.

#[derive(Debug, Deserialize, Serialize)]
struct Items {
    items: Vec<Message>,
}

#[derive(Debug, Deserialize, Serialize)]
struct Message {
    #[serde(rename = "Message")]
    msg: Payload,
}

#[derive(Debug, Deserialize, Serialize)]
struct Payload {
    header: String,
    data: String,
}

I deserialize like that:

let x = json_string; // the above json
let msg: Items = serde_json::from_str(&x)?;

This works but maybe it could be made cleaner. I thought of something like this(which does not work) so that i could remove the unnecessary structs(Items, Message):

let msg: Vec<Payload> = serde_json::from_str(&x)?;

Solution

  • There is 3 solutions:

    1. Deal with it
    2. Implement custom Deserialize
    3. Use unsafe and #[repr(transparent)]:
    use serde::{Deserialize, Serialize};
    
    #[derive(Debug, Deserialize, Serialize)]
    #[repr(transparent)]
    struct Items {
        items: Vec<Message>,
    }
    
    #[derive(Debug, Deserialize, Serialize)]
    #[repr(transparent)]
    struct Message {
        #[serde(rename = "Message")]
        msg: Payload,
    }
    
    #[derive(Debug, PartialEq, Deserialize, Serialize)]
    struct Payload {
        header: String,
        data: String,
    }
    
    fn main() {
        let raw = r#"{
        "items": [{
            "Message": {
                "data": "data",
                "header": "header"
            },
            "structx": {
                "val": 1
            },
            "val1": "test"
        }]
    }"#;
    
        let msg: Items = serde_json::from_str(&raw).unwrap();
        let payloads: Vec<Payload> = unsafe { std::mem::transmute(msg) };
    
        assert_eq!(
            payloads,
            vec![Payload {
                data: String::from("data"),
                header: String::from("header")
            }]
        );
    }