'Serialize/Deserialize json array to struct

I have incoming data in a json array that I deserialize into a struct, but I can't seem figure out how to serialize it back into an array instead of an object.

Do I have to implement a custom serializer here or is there some other serde attribute I can add?

I have looked through the documentation on serde.rs but I can't seem to find anything that would solve this.

#[cfg(test)]
mod tests {

    use serde_json;

    #[test]
    fn test_new() {
        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase", untagged)]
        enum Kind {
            Authorization(AuthorizationKind),
            Notification(NotificationKind),
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase", untagged)]
        enum AuthorizationKind {
            Request(AuthorizationRequest),
            Response(AuthorizationResponse),
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct AuthorizationRequest {
            request: String,
            code: usize,
            secret: String,
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct AuthorizationResponse {
            response: String,
            code: usize,
            authorized: bool,
            token: Token,
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct Token {
            token: String,
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        enum NotificationKind {
            Request(NotificationRequest),
            Response(NotificationResponse),
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct NotificationRequest {
            request: String,
            code: usize,
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct NotificationResponse {
            response: String,
        }

        let json = r#"["authorize request incoming",2,"TH15 15 MY! 53CR3T"]"#;

        let request: Result<Kind, _> = serde_json::from_str(&json);
        assert_eq!(request.is_ok(), true);

        let request = request.unwrap();
        let back_to_json = serde_json::to_string(&request).unwrap();
        println!("{back_to_json}");

        assert_eq!(json, back_to_json);
    }
}

Edit 1: I ended up implementing a custom serializer, not sure it's the best approach though.

#[cfg(test)]
mod tests {

    #[test]
    fn test_new() {
        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase", untagged)]
        enum Kind {
            Authorization(AuthorizationKind),
            Notification(NotificationKind),
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase", untagged)]
        enum AuthorizationKind {
            Request(AuthorizationRequest),
            Response(AuthorizationResponse),
        }

        #[derive(serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct AuthorizationRequest {
            request: String,
            code: usize,
            secret: String,
        }

        impl TryFrom<Kind> for AuthorizationRequest {
            type Error = &'static str;
            fn try_from(value: Kind) -> Result<Self, Self::Error> {
                match value {
                    Kind::Authorization(auth) => match auth {
                        AuthorizationKind::Request(req) => Ok(AuthorizationRequest {
                            request: req.request,
                            code: req.code,
                            secret: req.secret,
                        }),
                        _ => Err("Failed to create type"),
                    },
                    _ => Err("Failed to create type"),
                }
            }
        }

        impl serde::Serialize for AuthorizationRequest {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::Serializer,
            {
                (&self.request, self.code, &self.secret).serialize(serializer)
            }
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct AuthorizationResponse {
            response: String,
            code: usize,
            authorized: bool,
            token: Token,
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct Token {
            token: String,
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        enum NotificationKind {
            Request(NotificationRequest),
            Response(NotificationResponse),
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct NotificationRequest {
            request: String,
            code: usize,
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct NotificationResponse {
            response: String,
        }

        let json = r#"["authorize request incoming",2,"TH15 15 MY! 53CR3T"]"#;

        let request: Result<Kind, _> = serde_json::from_str(&json);
        assert_eq!(request.is_ok(), true);

        let request = request.unwrap();
        let authorization_request: AuthorizationRequest = request.try_into().unwrap();
        let back_json = serde_json::to_string(&authorization_request).unwrap();
        assert_eq!(json, back_json);
    }
}

Edit 3

Even better is to change AuthorizationKind and NotificationKind types to tuples. This will make it easy to serialize into a json array.

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase", untagged)]
        enum AuthorizationKind {
            Request(String, usize, String),
            Response(String, usize, bool, Token),
        }
...
        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        enum NotificationKind {
            Request(String, usize),
            Response(String),
        }


Solution 1:[1]

The problem is relying in the ordering, you would have to keep track of the keys (I'm adding an example but you could chose something else), then you can turn a json::Value (object) into an array iterating over the keys:


#[cfg(test)]
mod tests {

    use serde_json;

    #[test]
    fn test_new() {
        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase", untagged)]
        enum Kind {
            Authorization(AuthorizationKind),
            Notification(NotificationKind),
        }

        impl Kind {
            fn keys(&self) -> Vec<&str> {
                match self {
                    Self::Authorization(a) => a.keys(),
                    Self::Notification(n) => n.keys(),
                }
            }
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase", untagged)]
        enum AuthorizationKind {
            Request(AuthorizationRequest),
            Response(AuthorizationResponse),
        }

        impl AuthorizationKind {
            fn keys(&self) -> Vec<&str> {
                match self {
                    Self::Request(r) => r.keys(),
                    Self::Response(r) => r.keys(),
                }
            }
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct AuthorizationRequest {
            request: String,
            code: usize,
            secret: String,
        }

        impl AuthorizationRequest {
            fn keys(&self) -> Vec<&str> {
                vec!["request", "code", "secret"]
            }
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct AuthorizationResponse {
            response: String,
            code: usize,
            authorized: bool,
            token: Token,
        }

        impl AuthorizationResponse {
            fn keys(&self) -> Vec<&str> {
                vec!["response", "code", "authorized", "token"]
            }
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct Token {
            token: String,
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        enum NotificationKind {
            Request(NotificationRequest),
            Response(NotificationResponse),
        }

        impl NotificationKind {
            fn keys(&self) -> Vec<&str> {
                todo!()
            }
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct NotificationRequest {
            request: String,
            code: usize,
        }

        impl NotificationRequest {
            fn keys(&self) -> Vec<&str> {
                todo!()
            }
        }

        #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
        #[serde(rename_all = "camelCase")]
        struct NotificationResponse {
            response: String,
        }

        impl NotificationResponse {
            fn keys(&self) -> Vec<&str> {
                todo!()
            }
        }

        let json = r#"["authorize request incoming",2,"TH15 15 MY! 53CR3T"]"#;

        let request: Result<Kind, _> = serde_json::from_str(&json);
        assert_eq!(request.is_ok(), true);

        let request = request.unwrap();
        let back_to_json = serde_json::to_value(&request).unwrap();
        let as_array = serde_json::to_string(
            &request
                .keys()
                .iter()
                .map(|k| back_to_json.get(k).unwrap())
                .collect::<Vec<_>>(),
        )
        .unwrap();
        println!("{as_array}");

        assert_eq!(json, as_array);
    }
}

Playground

It is not the best solution (but a working one), a lot of thing could go wrong on mismatches and refactors. If possible serialize your structures as objects instead of arrays.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Netwave