Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unmarshaling XML in Go with Conflicting Element Names

I have the following XML, externally defined and outside of my organization's control:

<foo>
  <bar>
    <zip>zip</zip>
  </bar>
  <bar>
    <zap>zap</zap>
  </bar>
</foo>

I am using these structs:

type Foo struct {
    XMLName xml.Name `xml:"foo"`
    Bar1    Bar1
    Bar2    Bar2
}

type Bar1 struct {
    XMLName xml.Name `xml:"bar"`
    Zip     string   `xml:"zip"`
}

type Bar2 struct {
    XMLName xml.Name `xml:"bar"`
    Zap     string   `xml:"zap"`
}

Because of the conflicting 'bar' name, nothing gets unmarshaled. How can I populate the Bar1 and Bar2 structs?

This is what I have: https://play.golang.org/p/D2IRLojcTB

This is the result I want: https://play.golang.org/p/Ytrbzzy9Ok

In the second one, I have updated the second 'bar' to be 'bar1,' and it all works. I'd rather come up with a cleaner solution that modifying the incoming XML.

like image 697
user3268232 Avatar asked Mar 10 '15 00:03

user3268232


1 Answers

The encoding/xml package won't be able to do exactly what you want, since it makes the decision over which field of Foo to decode into when it encounters the <bar> element, rather than when processing children of that element. Your struct definitions make this decision ambiguous, as the error from xml.Unmarshal indicates:

main.Foo field "Bar1" with tag "" conflicts with field "Bar2" with tag ""

Here are two alternatives that will work though:

1. Use one Bar struct to cover both branches

If you modify your types to read as:

type Foo struct {
    XMLName xml.Name `xml:"foo"`
    Bars    []Bar    `xml:"bar"`
}

type Bar struct {
    Zip string `xml:"zip"`
    Zap string `xml:"zap"`
}

You will now get a slice that represents all the <bar> elements. You can tell whether the element had a <zip> or <zap> element by checking whether the corresponding fields are non-empty.

You can try out this version here: https://play.golang.org/p/kguPCYmKX0

2. Use child selectors

If you are only interested in a single child element of <bar> in each branch, then you might not need a struct to represent that element at all. For example, you could decode into the following type:

type Foo struct {
    XMLName xml.Name `xml:"foo"`
    Zip     string   `xml:"bar>zip"`
    Zap     string   `xml:"bar>zap"`
}

Now the children of the <bar> elements will be decoded directly into members of the Foo struct. Note that with this option you won't be able to distinguish your chosen input from e.g.

<foo>
  <bar>
    <zip>zip</zip>
    <zap>zap</zap>
  </bar>
</foo>

If that will cause problems, then you should pick the first solution.

You can try out this version here: https://play.golang.org/p/fAE_HSrv4y

like image 53
James Henstridge Avatar answered Sep 25 '22 19:09

James Henstridge