media-io / yaserde

Yet Another Serializer/Deserializer
MIT License
175 stars 57 forks source link

namespace prefix seems to be required for text but not attributes #100

Open cdwilson opened 3 years ago

cdwilson commented 3 years ago

I'm trying to use yaserde (for the first time) to deserialize XML that has a default namespace like this:

<root xmlns="http://example.com/ns" attr="attr value">
    <text>test value</text>
</root>

Per the discussion in #92 it looks like the recommended way to handle this is to define a namespace prefix for the default namespace (prefix = "ns") and then apply that namespace prefix to each field in the struct via #[yaserde(prefix = "ns")].

However, it appears that text elements require the explicit namespace prefix, but attributes do not require it. Can you help me understand why it's not necessary to explicitly define the namespace prefix for attributes? And the reverse question: why is it necessary to explicitly define the namespace prefix for the text element if the default_namespace has already been defined?

Here's an example where prefix = "ns" has been applied to the text element, but not to the attr attribute and it works fine:

extern crate yaserde;
extern crate yaserde_derive;

use yaserde_derive::YaDeserialize;

#[derive(Debug, YaDeserialize)]
#[yaserde(
    prefix = "ns",
    default_namespace = "ns",
    namespace = "ns: http://example.com/ns"
)]
struct Root {
    #[yaserde(attribute)] // why is prefix not needed here?
    // #[yaserde(attribute, prefix = "ns")] // this appears to be equivalent to the line above
    attr: String,
    #[yaserde(prefix = "ns")]
    text: String,
}

fn main() {
    let content = r#"
<?xml version="1.0" encoding="UTF-8" ?>
<root xmlns="http://example.com/ns" attr="attr value">
    <text>test value</text>
</root>
    "#;
    let root: Root = yaserde::de::from_str(&content).unwrap();
    println!("{:#?}", root);
}

If I remove the prefix = "ns" from the text element, I get an error "bad namespace for text, found http://example.com/ns".

In general, it would be nice if instead of creating a fake namespace prefix that doesn't actually exist (ns in the example above), the default namespace could be handled implicitly, i.e. being able to just use something like this:

#[derive(Debug, YaDeserialize)]
#[yaserde(
    namespace = "http://example.com/ns"
)]
struct Root {
    #[yaserde(attribute)]
    attr: String,
    text: String,
}
frenata commented 1 year ago

Related issue that I just bumped into, which is that although the current suggestion "works" for deserialization, it ruins the ability to cleanly round-trip via serialization, since you'll get back ns:foo tags everywhere. :(

antis81 commented 3 months ago

Little late to the party. However if I understand it correctly we can just use the #yaserde(text) macro attribute to parse text child nodes.

#[derive(Debug, YaDeserialize)]
struct Root {
    #[yaserde(attribute)]
    attr: String,
    #[yaserde(text)]
    text: String,
}

Works for me. In my case there is no namespaces in the XML input file.