Search code examples
rustserde

How can I serialize a struct into a format that has dynamic keys in rust using Serde


I have a struct similar to

pub struct FooBarList {
    items: Vec<FooBar>,
    name: String
}

pub struct FooBar {
    pub foo: i32,
    pub bar: i32
}

I need to serialize this into a flattened format i.e. something like

{
    "name": "foo",
    "foo1": 0,
    "bar1": 41,
    "foo2": 43,
    "bar2": 12,
...
}

I tried doing the following:

impl Serialize for FooBarList {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let item_count = self.items.len();

        let mut struct_ser = serializer.serialize_struct("FooBarList", 1 + 2 * item_count)?;

        for (i, item) in (&self.items).into_iter().enumerate() {
            struct_ser.serialize_field(format!("foo{}", i + 1).as_str(), &item.foo)?;
            struct_ser.serialize_field(format!("bar{}", i + 1).as_str(), &item.bar)?;
        }

        struct_ser.serialize_field("name", &self.name)?;

        struct_ser.end()
    }
}

This fails however, because struct_ser.serialize_field() requires &'static str as the first parameter, but the &str produced by format!().as_str() has a non-static lifetime. I don't see a way to create the strings statically, as length of the number of items is only known at runtime. Is there any way to circumvent that limitation?


Solution

  • You can serialize it as a map using serialize_map():

    impl Serialize for FooBarList {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {
            let item_count = self.items.len();
    
            let mut struct_ser = serializer.serialize_map(Some(1 + 2 * item_count))?;
    
            for (i, item) in (&self.items).into_iter().enumerate() {
                struct_ser.serialize_entry(&format!("foo{}", i + 1), &item.foo)?;
                struct_ser.serialize_entry(&format!("bar{}", i + 1), &item.bar)?;
            }
    
            struct_ser.serialize_entry("name", &self.name)?;
    
            struct_ser.end()
        }
    }
    

    You can't give a name to the struct in this way, though.