media-io / yaserde

Yet Another Serializer/Deserializer
MIT License
174 stars 58 forks source link

Conflict with multiple elements with the same name but different prefixes #186

Open mtorromeo opened 2 months ago

mtorromeo commented 2 months ago

Hi, I'm trying to deserialize an XML which have multiple elements with the same name but defined in different namespaces and consequently using different prefixes.

The actual XSDs are rather complex, but the problem can be illustrated with this snippet:

<error>
  <reasonCode>1</reasonCode>
  <ext:reasonCode>1234</ext:reasonCode>
</error>

which I tried implementing with this struct:

pub struct ErrorType {
    #[yaserde(rename = "reasonCode", prefix = "myns")]
    pub reason_code: Option<u16>,
    #[yaserde(rename = "reasonCode", prefix = "ext")]
    pub ext_reason_code: Option<u16>,
}

but this results in the following error:

error[E0428]: the name `__Visitor_ReasonCode_` is defined multiple times

Is this supported?

mtorromeo commented 2 months ago

I got past this error with the following patch:

--- 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
       ),

But then I get a new error at runtime when trying to deserialize an element <ext:reasonCode>:

bad namespace for reasonCode, found http://example.com/ext

It seems like yaserde reads the "reasonCode" name and expects it to use the namespace of "myns" instead of considering the "ext" prefix?

mtorromeo commented 2 months ago

By looking at the expanded macro the issue is in the match statement on the name which is generated with 2 itentical branches:

                                match name.local_name.as_str() {
                                    "reasonCode" => {
                                        // ...
                                    }
                                    "reasonCode" => {
                                        // ...
                                    }
                                }

so it will always only use the first definition