media-io / yaserde

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

Failed to parse with default namespace #92

Closed kaedwen closed 3 years ago

kaedwen commented 3 years ago

I need to parse the following

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns="http://www.w3c.org/abc" date="2020">
  <buecher>
    <buch id="123">
      <iban>123</iban>
    </buch>
    <buch id="456">
      <iban>456</iban>
    </buch>
  </buecher>
</root>

but i get the error "bad namespace for iban, found http://www.w3c.org/abc"

extern crate yaserde;

#[macro_use]
extern crate yaserde_derive;

use std::io::Read;
use yaserde::YaDeserialize;

use std::fs::File;
use std::io::BufReader;

#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
#[derive(Debug, YaDeserialize)]
struct Root {
  date: String,
  buecher: Option<Buecher>,
}

#[derive(Debug, YaDeserialize)]
struct Buecher {
  buch: Vec<Buch>,
}

#[derive(Debug, YaDeserialize)]
struct Buch {
  id: String,
  iban: String,
}

fn main() {
  let src_file = File::open("data/test.xml");
  if src_file.is_ok() {
    let _buf_reader = BufReader::new(src_file.unwrap());
    let _data: Root = yaserde::de::from_reader(_buf_reader).unwrap();
    println!("{:#?}", _data);
  }
}

also duplicating the yaserde definition to every struct does not help

MarcAntoine-Arnaud commented 3 years ago

Hello @hei-pa

YaSerDe (in derive in general) generate code close to the structure for the structure. So it's impossible to know the namespace for child elements.

To fix the namespace issue it requires to define for each structure the namespace definition like:


#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
#[derive(Debug, YaDeserialize)]
struct Root {
  date: String,
  buecher: Option<Buecher>,
}

#[derive(Debug, YaDeserialize)]
#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
struct Buecher {
  buch: Vec<Buch>,
}

#[derive(Debug, YaDeserialize)]
#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
struct Buch {
  id: String,
  iban: String,
}

In addition, to generate the same output (with the default namespace), you have to select the default_namespace, so it will be:

#[yaserde(
  prefix = "ns",
  default_namespace = "ns"
  namespace = "ns: http://www.w3c.org/abc"
)]

If it works for you, I'll invite you to close the issue ;-) Best Marc-Antoine

kaedwen commented 3 years ago

Yes I have read something similar -- but adding

#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]

to every struct does not solve the issue. The error message remains

"bad namespace for iban, found http://www.w3c.org/abc"'
kaedwen commented 3 years ago

ok I found a solution. I had to add the #[yaserde] to the iban field directly ?!?

#[derive(Debug, YaDeserialize)]
#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
struct Root {
  #[yaserde(attribute)]
  date: String,
  buecher: Option<Buecher>,
}

#[derive(Debug, YaDeserialize)]
#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
struct Buecher {
  buch: Vec<Buch>,
}

#[derive(Debug, YaDeserialize)]
#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
struct Buch {
  #[yaserde(attribute)]
  id: String,
  #[yaserde(
    prefix = "ns",
    namespace = "ns: http://www.w3c.org/abc"
  )]
  iban: String,
}

Is there an other solution? I thought it's enough to add it to the struct itself.

Why do I have to add it to the iban field but not to buecher or buch?

MarcAntoine-Arnaud commented 3 years ago

Hoo yeah you have to define for each field, but only prefix is required like:

#[derive(Debug, YaDeserialize)]
#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
struct Root {
  #[yaserde(attribute)]
  date: String,
  buecher: Option<Buecher>,
}

#[derive(Debug, YaDeserialize)]
#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
struct Buecher {
  buch: Vec<Buch>,
}

#[derive(Debug, YaDeserialize)]
#[yaserde(
  prefix = "ns",
  namespace = "ns: http://www.w3c.org/abc"
)]
struct Buch {
  #[yaserde(attribute)]
  id: String,
  #[yaserde(prefix = "ns")]
  iban: String,
}

It's important to specify for each field because it can be a mix of namespaces, to stay very generic...

kaedwen commented 3 years ago

ok thank you that's great