serde_with icon indicating copy to clipboard operation
serde_with copied to clipboard

Serialize struct with additional root element

Open jonasbb opened this issue 4 years ago • 4 comments

Sometimes structs/enums need an additional root element. This can be done with a single element enum like so:

#[derive(Serialize)]
enum Foobar {
    #[serde(rename = "root_field_name")]
    RootName{
        field_a: bool,
        field_b: u32,
    }
}

// as JSON
{
  "root_field_name": {
    "field_a": false,
    "field_b": 123
  }
}

A better solution would be a macro which adds the root element without requiring changing the struct.

Prior art

  • https://github.com/serde-rs/serde/issues/1345
  • https://crates.io/crates/serde_struct_wrapper

jonasbb avatar Aug 07 '21 18:08 jonasbb

#![feature(adt_const_params)]

#[derive(Copy, Clone, Debug, Default)]
pub struct Layer<const FIELD: &'static str, T: ?Sized>(T);

impl<const FIELD: &'static str, T, U> SerializeAs<T> for Layer<FIELD, U>
where
    U: SerializeAs<T>,
    T: ?Sized,
    U: ?Sized,
{
    fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        use serde::ser::SerializeStruct;

        let mut ser_struct = serializer.serialize_struct("Layer", 1)?;
        ser_struct.serialize_field(FIELD, &ser::SerializeAsWrap::<T, U>::new(source))?;
        ser_struct.end()
    }
}

// Can be used like this
#[serde_as]
#[derive(Serialize)]
struct Data {
    i: i32,
    #[serde_as(as = r#"Layer<"extra", Layer<"foobar", Layer<"really_another_one", DisplayFromStr>>>"#)]
    b: bool,
}

// or like
serde_json::to_string(&Layer::<"root">(&data))

https://github.com/dtolnay/monostate contains a solution to encode &str into a static type without requiring adt_const_params. It doesn't quite fit the above use case, since the value is not accessible as a &'static str. But Layer could be implemented if it uses serialize_map.

jonasbb avatar Nov 04 '21 22:11 jonasbb

Would like to see this feature implemented still

swisscheesy avatar Feb 10 '23 08:02 swisscheesy

Would like to see this feature implemented still

I would like to see this implemented too, that is why the issue is still open. But nothing has changed so far. serde will not implement it. A proper alternative would require forking serde_derive. The linked crate in the top post requires too many compromises. And the other post makes use of an unstable feature. So for the foreseeable future, nothing will happen here.

jonasbb avatar Feb 10 '23 21:02 jonasbb

This seems related to the general issue of parameterize SerializeAs structs.

For example, for a database I have an sender: String field I want serialized as { table: "sender", id: "<sender>"} (I then learned strings cannot be used as const generics atm)

blueforesticarus avatar Feb 28 '23 06:02 blueforesticarus