取消封送 XML:根据属性值使用不同的目标类型

  • 本文关键字:类型 目标 属性 XML 取消 go
  • 更新时间 :
  • 英文 :


我想使用不同的类型根据父节点的 name 属性取消封送子节点的 XML 内容。

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

这是操场,对问题进行了基本设置。

<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类型的节点,并通过打开它们的Name属性来创建ApplePeach结构:

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

这是一个游乐场链接。

另一个更复杂的解决方案(但也更优雅和实用(是在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)
}

这是第二个解决方案的游乐场链接

结果:

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

最新更新