解组XML:根据属性值使用不同的目标类型

时间:2019-06-17 05:41:39

标签: go

我想基于父节点的name属性使用其他类型来解组子节点的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
}

1 个答案:

答案 0 :(得分:2)

只需打开Element属性,即可简单地遍历Apple类型的节点并创建PeachName结构:

    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

另一种更复杂的解决方案(但也更优雅和实用)是在UnmarshalXML类型上实现自己的Element方法,该方法将使用适当的类型直接填充该方法:

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}]