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),
}
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: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.