首页 > 解决方案 > Go中具有动态类型(空接口)的XML Unmarshal

问题描述

我需要解析具有动态元素的 XML 消息,因此我interface{}在 Message 结构中使用了类型元素来表示它。

一旦我知道了这个动态元素的类型(在运行时),我就会初始化一个消息结构,然后尝试解组 XML 消息。但是,动态元素的内容并未解组。

这是一个我想要实现的 Go Playground,带有评论和实际与预期的输出: https: //play.golang.org/p/eKVetUPmVI2

我尝试了几种变体,但无法让解组按预期工作。谁能帮我理解为什么这种行为以及如何使它起作用?提前致谢。

代码(以防 Go Playground 链接有一天中断):

package main

import "fmt"
import "encoding/xml"

// XML root
type Message struct {
    XMLName   xml.Name  `xml:"message"`
    Operation Operation `xml:"operation"`
}

// An Operation can contain either a Create or an Update element
type Operation struct {
    Create *Create `xml:"create"`
    Update *Update `xml:"update"`
}

// Doesn't matter...
type Create struct{}

// Update contains a Color element or Any other element (we only know its type during runtime)
type Update struct {
    Color *Color `xml:"color"`
    Other Any
}

// Doesn't matter...
type Color struct{}

type Any interface{}

var xmlStr = []byte(`<message>
    <operation>
        <update>
            <size>
                <width>1000</width>
            </size>
        </update>
    </operation>
</message>`)

func main() {
    // At this point we already know what to expect to receive in Other, so we can declare a struct for its content (Size)
    type Size struct {
        XMLName xml.Name `xml:"size"`
        Width   string   `xml:"width"`
    }

    // Unmarshal
    msg := &Message{
        Operation: Operation{
            Update: &Update{
                Other: &Size{}, // Here I'm setting Other to Size, so I would expect Go to unmarshal the <size> contents into it
            },
        },
    }
    if err := xml.Unmarshal(xmlStr, msg); err != nil {
        fmt.Println(err)
    }

    // Marshal again
    b, err := xml.MarshalIndent(msg, "", "    ")
    if err != nil {
        fmt.Println(err)
    }

    fmt.Printf("expected:\n\n%s\n\n", xmlStr)
    fmt.Printf("actual:\n\n%s", string(b))
}

标签: go

解决方案


根据encoding/xml包文档:

如果 XML 元素包含与上述任何规则都不匹配的子元素,并且结构具有带有 tag 的字段",any",则 unmarshal 会将子元素映射到该结构字段。

对您的代码进行一个小的更新,使其按您预期的方式工作:

xml:",any"标签添加到您的Other字段定义中。

为了清理代码,我也会删除Any类型,你不需要它。您可以将Other字段定义更改为interface{}使用标签键入xml:",any"并完成相同的操作。

像这样:

Other interface{} `xml:",any"`

执行并查看捕获的“1000”。

我建议更新您的问题以直接包含您的代码,以使人们更容易找到/搜索/阅读您的问题。拥有 Go 游乐场链接也很有用,因此读者可以快速运行/调整/测试示例。


推荐阅读