From 0b8a7968f10f3348d26950aeaa780e0187010565 Mon Sep 17 00:00:00 2001 From: yoann beaugnon Date: Mon, 8 Apr 2024 16:39:06 +0200 Subject: [PATCH 1/2] Add a derive attribute for deserialization of enum internally tagged via an attribute --- yaserde/tests/enum.rs | 36 ++++++++++++++++++++++++++ yaserde_derive/src/common/attribute.rs | 10 +++++++ yaserde_derive/src/de/expand_enum.rs | 16 +++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/yaserde/tests/enum.rs b/yaserde/tests/enum.rs index 95991bd..dd2e5fa 100644 --- a/yaserde/tests/enum.rs +++ b/yaserde/tests/enum.rs @@ -357,3 +357,39 @@ fn unnamed_enum() { serialize_and_validate!(model, content); deserialize_and_validate!(content, model, XmlStruct); } + +#[test] +fn tagged_enum() { + #[derive(Debug, PartialEq, YaSerialize, YaDeserialize, Default)] + #[yaserde(tag = "type")] + enum XmlEnum { + #[default] + #[yaserde(rename = "foo")] + Foo, + #[yaserde(rename = "bar")] + Bar, + } + + #[derive(Debug, PartialEq, YaSerialize, YaDeserialize, Default)] + struct XmlStruct { + #[yaserde(rename = "foobar")] + foo_bar: XmlEnum, + } + + let model = XmlEnum::Foo; + let content = ""; + // serialize_and_validate!(model, content); + deserialize_and_validate!(content, model, XmlEnum); + + let model = XmlEnum::Bar; + let content = ""; + // serialize_and_validate!(model, content); + deserialize_and_validate!(content, model, XmlEnum); + + let model = XmlStruct { + foo_bar: XmlEnum::Foo, + }; + let content = ""; + // serialize_and_validate!(model, content); + deserialize_and_validate!(content, model, XmlStruct); +} diff --git a/yaserde_derive/src/common/attribute.rs b/yaserde_derive/src/common/attribute.rs index be4361f..bf5bcdb 100644 --- a/yaserde_derive/src/common/attribute.rs +++ b/yaserde_derive/src/common/attribute.rs @@ -12,6 +12,7 @@ pub struct YaSerdeAttribute { pub namespaces: BTreeMap, String>, pub prefix: Option, pub rename: Option, + pub tag: Option, pub skip_serializing: bool, pub skip_serializing_if: Option, pub text: bool, @@ -40,6 +41,7 @@ impl YaSerdeAttribute { let mut namespaces = BTreeMap::new(); let mut prefix = None; let mut rename = None; + let mut tag = None; let mut skip_serializing = false; let mut skip_serializing_if = None; let mut text = false; @@ -82,6 +84,9 @@ impl YaSerdeAttribute { "rename" => { rename = get_value(&mut attr_iter); } + "tag" => { + tag = get_value(&mut attr_iter); + } "skip_serializing" => { skip_serializing = true; } @@ -107,6 +112,7 @@ impl YaSerdeAttribute { namespaces, prefix, rename, + tag, skip_serializing, skip_serializing_if, text, @@ -182,6 +188,7 @@ fn parse_empty_attributes() { namespaces: BTreeMap::new(), prefix: None, rename: None, + tag: None, skip_serializing: false, skip_serializing_if: None, text: false, @@ -232,6 +239,7 @@ fn parse_attributes() { namespaces: BTreeMap::new(), prefix: None, rename: None, + tag: None, skip_serializing: false, skip_serializing_if: None, text: false, @@ -282,6 +290,7 @@ fn only_parse_yaserde_attributes() { namespaces: BTreeMap::new(), prefix: None, rename: None, + tag: None, skip_serializing: false, skip_serializing_if: None, text: false, @@ -338,6 +347,7 @@ fn parse_attributes_with_values() { namespaces, prefix: None, rename: None, + tag: None, skip_serializing: false, skip_serializing_if: None, text: false, diff --git a/yaserde_derive/src/de/expand_enum.rs b/yaserde_derive/src/de/expand_enum.rs index a8994f9..5a41c57 100644 --- a/yaserde_derive/src/de/expand_enum.rs +++ b/yaserde_derive/src/de/expand_enum.rs @@ -24,6 +24,20 @@ pub fn parse( let flatten = root_attributes.flatten; + let element_name = if let Some(tag) = &root_attributes.tag { + quote! { + attributes + .iter() + .find(|attr| attr.name.local_name.as_str() == #tag) + .ok_or(format!("Expected enum tagged with {}, found {:?}", #tag, event))? + .value.as_str() + } + } else { + quote! { + name.local_name.as_str() + } + }; + quote! { impl ::yaserde::YaDeserialize for #name { #[allow(unused_variables)] @@ -50,7 +64,7 @@ pub fn parse( ::yaserde::__derive_trace!("Enum {} @ {}: matching {:?}", stringify!(#name), start_depth, event); match event { ::yaserde::__xml::reader::XmlEvent::StartElement { ref name, ref attributes, .. } => { - match name.local_name.as_str() { + match #element_name { #match_to_enum _named_element => { let _root = reader.next_event(); From f8eb0cd2a301326016cdbd15d091f385f63e4de2 Mon Sep 17 00:00:00 2001 From: yoann beaugnon Date: Mon, 29 Apr 2024 20:36:14 +0200 Subject: [PATCH 2/2] add support for serialization of enum tagged internally via an attribute --- yaserde/tests/enum.rs | 14 ++++++----- yaserde_derive/src/ser/expand_enum.rs | 35 ++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/yaserde/tests/enum.rs b/yaserde/tests/enum.rs index dd2e5fa..74de022 100644 --- a/yaserde/tests/enum.rs +++ b/yaserde/tests/enum.rs @@ -362,6 +362,7 @@ fn unnamed_enum() { fn tagged_enum() { #[derive(Debug, PartialEq, YaSerialize, YaDeserialize, Default)] #[yaserde(tag = "type")] + #[yaserde(rename = "foobar")] enum XmlEnum { #[default] #[yaserde(rename = "foo")] @@ -371,25 +372,26 @@ fn tagged_enum() { } #[derive(Debug, PartialEq, YaSerialize, YaDeserialize, Default)] + #[yaserde(rename = "base")] struct XmlStruct { #[yaserde(rename = "foobar")] foo_bar: XmlEnum, } let model = XmlEnum::Foo; - let content = ""; - // serialize_and_validate!(model, content); + let content = ""; + serialize_and_validate!(model, content); deserialize_and_validate!(content, model, XmlEnum); let model = XmlEnum::Bar; - let content = ""; - // serialize_and_validate!(model, content); + let content = ""; + serialize_and_validate!(model, content); deserialize_and_validate!(content, model, XmlEnum); let model = XmlStruct { foo_bar: XmlEnum::Foo, }; - let content = ""; - // serialize_and_validate!(model, content); + let content = ""; + serialize_and_validate!(model, content); deserialize_and_validate!(content, model, XmlStruct); } diff --git a/yaserde_derive/src/ser/expand_enum.rs b/yaserde_derive/src/ser/expand_enum.rs index 56eed3a..3ef0bed 100644 --- a/yaserde_derive/src/ser/expand_enum.rs +++ b/yaserde_derive/src/ser/expand_enum.rs @@ -26,6 +26,21 @@ pub fn serialize( .map(|variant| -> TokenStream { let _attrs = crate::common::YaSerdeAttribute::parse(&variant.attrs); + let add_tag = if let Some(tag) = &root_attributes.tag { + let attrs = crate::common::YaSerdeAttribute::parse(&variant.attrs); + let label = variant.ident.clone(); + let element_name = attrs.xml_element_name(&variant.ident); + quote! { + match self { + #name::#label { .. } => { + let tag = ::yaserde::__xml::name::OwnedName::local(#tag); + child_attributes.push(::yaserde::__xml::attribute::OwnedAttribute::new(tag, #element_name)); + } + _ => {} + } + } + } else { quote!() }; + let all_fields = variant .fields .iter() @@ -39,7 +54,7 @@ pub fn serialize( }) .collect(); - attribute_fields + let add_attributes : TokenStream = attribute_fields .iter() .map(|field| { let label = variant.ident.clone(); @@ -79,7 +94,9 @@ pub fn serialize( } } }) - .collect() + .collect(); + + quote!( #add_attributes #add_tag) }) .collect(); @@ -109,10 +126,16 @@ fn inner_enum_inspector( let label_name = build_label_name(label, &variant_attrs, &root_attributes.default_namespace); match variant.fields { - Fields::Unit => quote! { - &#name::#label => { - let data_event = ::yaserde::__xml::writer::XmlEvent::characters(#label_name); - writer.write(data_event).map_err(|e| e.to_string())?; + Fields::Unit => { + if let Some(_tag) = &root_attributes.tag { + quote! { #name::#label => {} } + } else { + quote! { + #name::#label => { + let data_event = ::yaserde::__xml::writer::XmlEvent::characters(#label_name); + writer.write(data_event).map_err(|e| e.to_string())?; + } + } } }, Fields::Named(ref fields) => {