Added support for serialization of structs as maps

This commit is contained in:
Armin Ronacher 2018-03-14 13:00:16 +01:00
parent 5a91ac5ba5
commit b4dbae250b

View File

@ -245,6 +245,13 @@ fn serialize_tuple_struct(
fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment {
assert!(fields.len() as u64 <= u64::from(u32::MAX));
match cattrs.repr() {
attr::ContainerRepr::Struct | attr::ContainerRepr::Auto => serialize_struct_as_struct(params, fields, cattrs),
attr::ContainerRepr::Map => serialize_struct_as_map(params, fields, cattrs),
}
}
fn serialize_struct_as_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment {
let serialize_fields = serialize_struct_visitor(
fields,
params,
@ -279,6 +286,64 @@ fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Contai
}
}
fn serialize_struct_as_map(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment {
let serialize_fields = serialize_struct_visitor(
fields,
params,
false,
&StructTrait::SerializeMap,
);
let mut serialized_fields = fields
.iter()
.filter(|&field| !field.attrs.skip_serializing())
.peekable();
let mut collect_extra = None;
if cattrs.unknown_fields_into().is_some() {
if let Some(field) = fields
.iter()
.filter(|field| field.attrs.collection_field())
.next()
{
let ident = &field.ident;
collect_extra = Some(quote! {
for (ref __key, ref __value) in &self.#ident {
try!(_serde::ser::SerializeMap::serialize_entry(
&mut __serde_state,
__key,
__value));
}
});
}
}
let let_mut = mut_if(serialized_fields.peek().is_some());
let len = if collect_extra.is_some() {
quote!(None)
} else {
let len = serialized_fields
.map(|field| match field.attrs.skip_serializing_if() {
None => quote!(1),
Some(path) => {
let ident = field.ident.expect("struct has unnamed fields");
let field_expr = get_member(params, field, &Member::Named(ident));
quote!(if #path(#field_expr) { 0 } else { 1 })
}
})
.fold(quote!(0), |sum, expr| quote!(#sum + #expr));
quote!(Some(#len))
};
quote_block! {
let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(__serializer, #len));
#(#serialize_fields)*
#collect_extra
_serde::ser::SerializeMap::end(__serde_state)
}
}
fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Container) -> Fragment {
assert!(variants.len() as u64 <= u64::from(u32::MAX));
@ -885,12 +950,19 @@ fn serialize_struct_visitor(
match skip {
None => ser,
Some(skip) => {
let skip_func = struct_trait.skip_field(span);
quote! {
if !#skip {
#ser
} else {
try!(#skip_func(&mut __serde_state, #key_expr));
if let Some(skip_func) = struct_trait.skip_field(span) {
quote! {
if !#skip {
#ser
} else {
try!(#skip_func(&mut __serde_state, #key_expr));
}
}
} else {
quote! {
if !#skip {
#ser
}
}
}
}
@ -1011,6 +1083,7 @@ fn get_member(params: &Parameters, field: &Field, member: &Member) -> Tokens {
}
enum StructTrait {
SerializeMap,
SerializeStruct,
SerializeStructVariant,
}
@ -1018,6 +1091,9 @@ enum StructTrait {
impl StructTrait {
fn serialize_field(&self, span: Span) -> Tokens {
match *self {
StructTrait::SerializeMap => {
quote_spanned!(span=> _serde::ser::SerializeMap::serialize_entry)
}
StructTrait::SerializeStruct => {
quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field)
}
@ -1027,14 +1103,15 @@ impl StructTrait {
}
}
fn skip_field(&self, span: Span) -> Tokens {
fn skip_field(&self, span: Span) -> Option<Tokens> {
match *self {
StructTrait::SerializeStruct => {
StructTrait::SerializeMap => None,
StructTrait::SerializeStruct => Some({
quote_spanned!(span=> _serde::ser::SerializeStruct::skip_field)
}
StructTrait::SerializeStructVariant => {
}),
StructTrait::SerializeStructVariant => Some({
quote_spanned!(span=> _serde::ser::SerializeStructVariant::skip_field)
}
})
}
}
}