From a067b85ee2ade328169f925d09b27882f2cd6117 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Arnaud Date: Sat, 31 Aug 2024 16:54:53 +0200 Subject: [PATCH] feat: handle same field name but different namespace issue #186 --- examples/src/bbigras_namespace.rs | 6 +-- examples/src/generic.rs | 2 + examples/src/lib.rs | 1 + .../src/same_element_different_namespaces.rs | 37 +++++++++++++++++++ yaserde_derive/src/common/field.rs | 26 ++++++++++++- yaserde_derive/src/de/expand_struct.rs | 12 ++++-- 6 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 examples/src/same_element_different_namespaces.rs diff --git a/examples/src/bbigras_namespace.rs b/examples/src/bbigras_namespace.rs index 3a24435..7ee144d 100644 --- a/examples/src/bbigras_namespace.rs +++ b/examples/src/bbigras_namespace.rs @@ -10,7 +10,7 @@ use yaserde::*; namespace = "html: http://www.w3.org/TR/REC-html40" )] struct Workbook { - #[yaserde(rename = "Worksheet")] + #[yaserde(rename = "Worksheet", prefix = "ss")] worksheet: Worksheet, } @@ -23,7 +23,7 @@ struct Workbook { namespace = "html: http://www.w3.org/TR/REC-html40" )] struct Worksheet { - #[yaserde(rename = "Table")] + #[yaserde(rename = "Table", prefix = "ss")] table: Table, #[yaserde(attribute, rename = "Name", prefix = "ss")] ws_name: String, @@ -53,7 +53,7 @@ struct Table { #[yaserde(attribute, rename = "DefaultRowHeight", prefix = "ss")] default_column_height: f32, - #[yaserde(rename = "Row")] + #[yaserde(rename = "Row", prefix = "ss")] rows: Vec, } diff --git a/examples/src/generic.rs b/examples/src/generic.rs index 0358382..08ba90c 100644 --- a/examples/src/generic.rs +++ b/examples/src/generic.rs @@ -28,6 +28,7 @@ where } #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] +#[yaserde(namespace = "u: urn:schemas-upnp-org:service:AVTransport:1")] pub struct SoapPlay { #[yaserde(rename = "Play", prefix = "u", default)] pub body: Play, @@ -93,6 +94,7 @@ fn test_for_generic_nested_struct() { }; let s = ser::to_string(&a).unwrap(); + println!("{s}"); let b: SoapEnvelope = de::from_str(&s).unwrap(); assert_eq!(a, b); diff --git a/examples/src/lib.rs b/examples/src/lib.rs index 8f83b28..1f50df2 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -2,4 +2,5 @@ mod bbigras_namespace; mod boscop; mod generic; mod ln_dom; +mod same_element_different_namespaces; mod svd; diff --git a/examples/src/same_element_different_namespaces.rs b/examples/src/same_element_different_namespaces.rs new file mode 100644 index 0000000..4223759 --- /dev/null +++ b/examples/src/same_element_different_namespaces.rs @@ -0,0 +1,37 @@ +// related to issue https://github.com/media-io/yaserde/issues/186 +use yaserde::*; + +#[derive(YaDeserialize, Debug, PartialEq)] +#[yaserde( + namespace = "myns: http://my_namespace_1/", + namespace = "ext: http://my_namespace_2/", + prefix = "myns" +)] +pub struct ErrorType { + #[yaserde(rename = "reasonCode", prefix = "myns")] + pub reason_code: Option, + #[yaserde(rename = "reasonCode", prefix = "ext")] + pub ext_reason_code: Option, +} + +#[test] +fn same_element_different_namespaces() { + use yaserde::de::from_str; + + let content = r#" + + 12 + 32 + + "#; + + let loaded: ErrorType = from_str(content).unwrap(); + println!("{:?}", loaded); + + let reference = ErrorType { + reason_code: Some(12), + ext_reason_code: Some(32), + }; + + assert_eq!(loaded, reference); +} diff --git a/yaserde_derive/src/common/field.rs b/yaserde_derive/src/common/field.rs index ec5e5ba..2dd2320 100644 --- a/yaserde_derive/src/common/field.rs +++ b/yaserde_derive/src/common/field.rs @@ -90,6 +90,13 @@ impl YaSerdeField { }, ); + let prefix = self + .attributes + .prefix + .clone() + .map(|p| format!("{}_", p.to_upper_camel_case())) + .unwrap_or_default(); + let attribute = self .attributes .attribute @@ -98,7 +105,8 @@ impl YaSerdeField { Ident::new( &format!( - "__Visitor_{attribute}{}_{}", + "__Visitor_{attribute}{}{}_{}", + prefix, label.replace('.', "_").to_upper_camel_case(), struct_id ), @@ -130,6 +138,20 @@ impl YaSerdeField { .map(|skip_serializing_if| Ident::new(skip_serializing_if, self.get_span())) } + pub fn prefix_namespace(&self, root_attributes: &YaSerdeAttribute) -> String { + root_attributes + .namespaces + .iter() + .find_map(|(prefix, namespace)| { + if self.attributes.prefix.eq(prefix) { + Some(namespace.clone()) + } else { + None + } + }) + .unwrap_or_default() + } + pub fn get_namespace_matching( &self, root_attributes: &YaSerdeAttribute, @@ -261,7 +283,7 @@ impl From<&syn::PathSegment> for Field { return Field::from(&path.path); } Some(syn::GenericArgument::Type(syn::Type::Group(syn::TypeGroup { elem, .. }))) => { - if let syn::Type::Path(ref group) = elem.as_ref() { + if let Path(ref group) = elem.as_ref() { return Field::from(&group.path); } } diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 782aa08..5000e8f 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -148,9 +148,11 @@ pub fn parse( let value_label = field.get_value_label(); let label_name = field.renamed_label_without_namespace(); + let namespace = field.prefix_namespace(root_attributes); + let visit_struct = |struct_name: syn::Path, action: TokenStream| { Some(quote! { - #label_name => { + (#namespace, #label_name) => { if depth == 0 { // Don't count current struct's StartElement as substruct's StartElement let _root = reader.next_event(); @@ -423,7 +425,9 @@ pub fn parse( let event = reader.next_event()?; #write_unused } else { - match name.local_name.as_str() { + let namespace = name.namespace.clone().unwrap_or_default(); + + match (namespace.as_str(), name.local_name.as_str()) { #call_visitors _ => { let event = reader.next_event()?; @@ -493,8 +497,10 @@ fn build_call_visitor( quote!(name.local_name.as_str()), ); + let namespace = field.prefix_namespace(root_attributes); + Some(quote! { - #label_name => { + (#namespace, #label_name) => { let visitor = #visitor_label{}; #namespaces_matching