serde_with
serde_with copied to clipboard
Serialize struct with additional root element
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
#![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.
Would like to see this feature implemented still
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.
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)