Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialize struct with additional root element #340

Open
jonasbb opened this issue Aug 7, 2021 · 4 comments
Open

Serialize struct with additional root element #340

jonasbb opened this issue Aug 7, 2021 · 4 comments
Labels
enhancement New feature or request

Comments

@jonasbb
Copy link
Owner

jonasbb commented Aug 7, 2021

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

@jonasbb jonasbb added the enhancement New feature or request label Aug 7, 2021
@jonasbb
Copy link
Owner Author

jonasbb commented Nov 4, 2021

#![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.

@swisscheesy
Copy link

Would like to see this feature implemented still

@jonasbb
Copy link
Owner Author

jonasbb commented Feb 10, 2023

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.

@blueforesticarus
Copy link

blueforesticarus commented Feb 28, 2023

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants