在golang中使用xml
包我无法解组非同类型列表。请考虑以下XML文档,其嵌套元素是非同类型列表:
<mydoc>
<foo>Foo</foo>
<bar>Bar</bar>
<foo>Another Foo</foo>
<foo>Foo #3</foo>
<bar>Bar 2</bar>
</mydoc>
以下用于测试XML un / marshalling的golang代码(也在the go playground上):
package main
import "encoding/xml"
import "fmt"
const sampleXml = `
<mydoc>
<foo>Foo</foo>
<bar>Bar</bar>
<foo>Another Foo</foo>
<foo>Foo #3</foo>
<bar>Bar 2</bar>
</mydoc>
`
type MyDoc struct {
XMLName xml.Name `xml:"mydoc"`
Items []Item
}
type Item interface {
IsItem()
}
type Foo struct {
XMLName xml.Name `xml:"foo"`
Name string `xml:",chardata"`
}
func (f Foo) IsItem() {}
type Bar struct {
XMLName xml.Name `xml:"bar"`
Nombre string `xml:",chardata"`
}
func (b Bar) IsItem() {}
func main() {
doMarshal()
doUnmarshal()
}
func doMarshal() {
myDoc := MyDoc{
Items: []Item{
Foo{Name: "Foo"},
Bar{Nombre: "Bar"},
Foo{Name: "Another Foo"},
Foo{Name: "Foo #3"},
Bar{Nombre: "Bar 2"},
},
}
bytes, err := xml.MarshalIndent(myDoc, "", " ")
if err != nil {
panic(err)
}
// Prints an XML document just like "sampleXml" above.
println(string(bytes))
}
func doUnmarshal() {
myDoc := MyDoc{}
err := xml.Unmarshal([]byte(sampleXml), &myDoc)
if err != nil {
panic(err)
}
// Fails to unmarshal the "Item" elements into their respective structs.
fmt.Printf("ERR: %#v", myDoc)
}
您将看到doMarshal()
生成我期望的确切XML文档;但是,doUnmarshal()
无法反序列化&#34;项目&#34;元素分为各自的结构。我尝试了一些更改,但似乎没有任何内容可以让他们正确解组(为myDoc.Items
创建存储空间,将&#34;项目&#34;的类型更改为[]*Item
[和其他人] ,摆弄XML标签等。)
如何让xml.Unmarshal(...)
反序列化不相关类型的元素列表?
答案 0 :(得分:2)
正如其他评论所指出的,如果没有一些帮助,解码器无法处理接口字段。在容器上实现xml.Unmarshaller
将使其按照您的要求执行(playground上的完整工作示例):
func (md *MyDoc) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
md.XMLName = start.Name
// grab any other attrs
// decode inner elements
for {
t, err := d.Token()
if err != nil {
return err
}
var i Item
switch tt := t.(type) {
case xml.StartElement:
switch tt.Name.Local {
case "foo":
i = new(Foo) // the decoded item will be a *Foo, not Foo!
case "bar":
i = new(Bar)
// default: ignored for brevity
}
// known child element found, decode it
if i != nil {
err = d.DecodeElement(i, &tt)
if err != nil {
return err
}
md.Items = append(md.Items, i)
i = nil
}
case xml.EndElement:
if tt == start.End() {
return nil
}
}
}
return nil
}
这只是@evanmcdonnal建议的实现。所有这一切都是根据下一个令牌的名称实例化正确的Item
,然后用它调用d.DecodeElement()
(即让xml解码器完成繁重的工作)。
请注意,unmarshalled Items
是指针。如果你想要价值,你需要做更多的工作。为了正确处理错误或意外的输入数据,还需要进一步扩展。