为了账号安全,请及时绑定邮箱和手机立即绑定

Golang xml.Unmarshal 接口类型

Golang xml.Unmarshal 接口类型

Go
HUH函数 2021-11-22 10:38:21
xml在 golang 中使用该包时,我在解组非同质类型列表时遇到了麻烦。考虑以下 XML 文档,其嵌套元素是非同类类型的列表:<mydoc>  <foo>Foo</foo>  <bar>Bar</bar>  <foo>Another Foo</foo>  <foo>Foo #3</foo>  <bar>Bar 2</bar></mydoc>以及以下用于测试 XML 解组/编组的 golang 代码(也在go playground 上):package mainimport "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()无法将“Item”元素反序列化为它们各自的结构。我已经尝试了一些更改,但似乎没有什么能让它们正确地解组(创建存储myDoc.Items,将“项目”的类型更改为[]*Item[和其他],摆弄 XML 标签等)。任何想法如何xml.Unmarshal(...)反序列化不相关类型的元素列表?
查看完整描述

1 回答

?
富国沪深

TA贡献1790条经验 获得超9个赞

正如其他评论所指出的,解码器无法在没有帮助的情况下处理接口字段。xml.Unmarshaller在容器上实现将使它做你想做的事(操场上的完整工作示例):


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 解码器完成繁重的工作)。


请注意,未编组的Items是指针。如果你想要值,你需要做更多的工作。这还需要进一步扩展以正确处理错误或意外输入数据。


查看完整回答
反对 回复 2021-11-22
  • 1 回答
  • 0 关注
  • 175 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信