go - 解码 XML : use different target type based on an attribute value

标签 go

我想使用不同的类型来根据父节点的名称属性解码子节点的 XML 内容。

在下面的示例中,我有 2 个具有属性“apple”和“peach”的子节点。我想在属性为 “apple” 时使用类型 Apple,在属性为 “peach” 时使用 Peach。基本上 ApplePeach 具有非常不同的结构,所以这就是场景。我将如何实现该目标或建议的方法是什么?

这是 playground有了问题的基本设置。

<element>
    <node name="apple">
        <apple>
            <color>red<color>
        </apple>
    </node>
    <node name="peach"> 
        <peach>
            <size>medium</size>
        </peach>
    </node>
</element>
var x = `...` // xml
type Element struct {
    Nodes []struct{
        Name string `xml:"name,attr"`
    } `xml:"node"`
    Apple Apple
    Peach Peach
}
type Apple struct { // use this struct if name is "apple"
    Color string 
} 
type Peach struct { // use this struct if name is "peach"
    Size string
}
func main() {
    e := Element{}
    err := xml.Unmarshal([]byte(x), &e)
    if err != nil {
        panic(err)
    }   
    fmt.Println(e.Apple.Color)
    fmt.Println(e.Peach.Size
}

最佳答案

您可以简单地迭代Element 类型的节点并创建ApplePeach 结构,方法是打开它们名称属性:

    for _, element := range e.Nodes {
        switch element.Name {
        case "apple":
            apples = append(apples, Apple{})
        case "peach":
            peaches = append(peaches, Peach{})
        }
    }

Here is a playground link .

另一个更复杂的解决方案(但也更优雅和实用)是在您的 Element 类型上实现您自己的 UnmarshalXML 方法,这将直接用正确的类型:

type Apple struct {
    Color string
}
type Peach struct {
    Size string
}

type Fruits struct {
    Apples  []Apple
    Peaches []Peach
}

type Element struct {
    XMLName xml.Name `xml:"element"`
    Nodes   []struct {
        Name  string `xml:"name,attr"`
        Apple struct {
            Color string `xml:"color"`
        } `xml:"apple"`
        Peach struct {
            Size string `xml:"size"`
        } `xml:"peach"`
    } `xml:"node"`
}

func (f *Fruits) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var element Element
    d.DecodeElement(&element, &start)

    for _, el := range element.Nodes {
        switch el.Name {
        case "apple":
            f.Apples = append(f.Apples, Apple{
                Color: el.Apple.Color,
            })
        case "peach":
            f.Peaches = append(f.Peaches, Peach{
                Size: el.Peach.Size,
            })
        }
    }

    return nil
}

func main() {
    f := Fruits{}

    err := xml.Unmarshal([]byte(x), &f)
    if err != nil {
        panic(err)
    }

    fmt.Println("Apples:", f.Apples)
    fmt.Println("Peaches", f.Peaches)
}

Here is a playground link for this second solution

结果:

Apples: [{red}]
Peaches [{medium}]

关于go - 解码 XML : use different target type based on an attribute value,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56625287/

相关文章:

mongodb - 维护 mgo session 的最佳实践

go - 如何从外部 gRPC 客户端连接到 k8s 集群内部的 gRPC 服务

go - 解码十六进制时间戳,可能是文件时间

pointers - 如何在结构文字中将 bool 指针设置为 true?

macos - 去安装: no install location for directory outside GOPATH

sql - 在 golang 中扫描 SQL NULL 值

go - 单值上下文中的多个值

go - Golang 中的传递属性

struct - 多个结构开关?

字符串.替换器 : how to replace all substrings at once?