[serde] Convert structure and values when serializing an arbitrary message to a JSON string

Hi Community!
I'm trying to get my head around serde better, and I would like to understand if it covers cases like this.

  • Transform e.g. such structure of rust object [ ... {"name": "field_name", "value": 1} ... ] to JSON string like { ... "field_name": 1 ... } of arbitrary message
  • Use some descriptor object (unknown at compile) that contains custom rules on how to handle the specified field values, etc.

Note: I got help how to solve when we Deserialize the JSON - string. And now I am wondering how to solve this for the Serialize action

There is some code snippet with examples is in the attachment.
Thank you in advance!

use serde; // 1.0.130
use serde::{Serialize, Deserialize};
use serde_json; // 1.0.69
use std::collections::HashMap;

// The following structs define the structure of the message

#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
struct MessageValue {
    fields: Vec<FieldValue>
}
#[derive(Debug, Serialize, Deserialize)]
struct FieldValue {
    name: String,
    value: Value
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum Value {
    Int32(i32),
    Message(MessageValue)
}

// The "MessageDescriptor" will contain the information about which field is repeated etc
// Also other data which affect serialization will be included here.
// Eg {"repeated_msg": "repeated", "some_msg": "singular" ...}

#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
struct MessageDescriptor {
    data: HashMap<String, String>
}

// One idea - to define anothe one struct "MessageValueAsJSONStructure" and then serialize it.
// But I would like to understand if there is some builtin solution like "DeserializeSeed" but for Serialize
    
// impl MessageDescriptor {
//     fn convert_to_repeated(&self, msg: MessageValue) -> MessageValueAsJSONStructure {
//         unimplemented!()
//     }
// }


fn main() {
    // Define the message
    let msg_obj = MessageValue {
        fields: vec![
            FieldValue {
                name: "some_int32".to_string(),
                value: Value::Int32(23)
            },
            FieldValue {
                name: "some_msg".to_string(),
                value: Value::Message(MessageValue{
                    fields: vec![
                        FieldValue{name:"some_int32".to_string(), value:Value::Int32(23)},
                    ]
                })
            },
            FieldValue {
                name: "repeated_msg".to_string(),
                value: Value::Message(MessageValue{
                    fields: vec![
                        FieldValue{name:"some_int32".to_string(), value:Value::Int32(23)},
                    ]
                })
            },
            FieldValue {
                name: "repeated_msg".to_string(),
                value: Value::Message(MessageValue{
                    fields: vec![
                        FieldValue{name: "some_int32".to_string(), value: Value::Int32(27)},
                        FieldValue{name: "some_int32".to_string(), value: Value::Int32(29)},
                    ]
                })
            },
        ]
    };
    let msg_ser = serde_json::to_string(&msg_obj).expect("Serialize the Message");
    println!("OUTPUT: {:?}", msg_ser);
}

(Playground)

Output:

  [
      {
        "name": "some_int32",
        "value": 23
      },
      {
        "name": "some_msg",
        "value": [
          {
            "name": "some_int32",
            "value": 23
          }
        ]
      },
      {
        "name": "repeated_msg",
        "value": [
          {
            "name": "some_int32",
            "value": 23
          }
        ]
      },
      {
        "name": "repeated_msg",
        "value": [
          {
            "name": "some_int32",
            "value": 27
          },
          {
            "name": "other_int32",
            "value": 29
          }
        ]
      }
    ]

Desired OUTPUT:

    {
      "some_int32": 23,
      "some_msg": {
        "some_int32": 23
      },
      "repeated_msg": [
        {
          "some_int32": 23
        },
        {
          "some_int32": 27,
          "other_int32": 29
        }
      ]
    }

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.34s
     Running `target/debug/playground`

  1. I inspired this discussion how to transfrom structure during serialization De/serialize struct as an array of values · Issue #637 · serde-rs/serde · GitHub

So, on my side I introduced Serialize implementations for my structure like

#[derive(Debug)]
struct MessageValue {
    fields: Vec<FieldValue>
}
impl Serialize for MessageValue {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        #[derive(Serialize)]
        struct Helper {
            data: HashMap<String, Value>
        }
        let mut helper = HashMap::new();
        for field in &self.fields {
            helper.insert(&field.name, &field.value);
        }
        helper.serialize(serializer)
    }
}

More details in [Playground]

  1. I think the same way I can implement Serialize for my descriptor object and somehow to do serialization for another structure inside implemention

You could have a look at Apache Avro (for rust, avrow). It allows you to define a scheme and use it for serialization and deserialization. I have not tested myself in Rust but I have used it for connecting Java with C++.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.