本文介绍使用 go 的 `encoding/xml` 包处理 xml 中无序、混合类型元素序列的方法,核心是利用 `xml:",any"` 标签结合 `xmlname` 字段保留原始顺序与类型信息。
在 Go 中解析具有“混合内容”(mixed content)的 XML——即同一父节点下按任意顺序、重复出现多种不同标签(如
Go 标准库提供了优雅的原生支持:通过 xml:",any" 通配标签,可将未知子元素统一捕获为一个 []interface{} 或更推荐的自定义中间结构 
package main
import (
"encoding/xml"
"fmt"
)
type RootNode struct {
XMLName xml.Name `xml:"RootNode"`
Elements []Element `xml:",any"` // ← 关键:捕获所有子元素,保持顺序
}
type Element struct {
XMLName xml.Name `xml:""` // ← 必须:自动填充实际标签名(如 ElementA)
// 可选:嵌入具体类型字段,按需解析内容
A *ElementA `xml:"ElementA"`
B *ElementB `xml:"ElementB"`
C *ElementC `xml:"ElementC"`
}
type ElementA struct {
Value string `xml:",chardata"`
}
type ElementB struct {
ID string `xml:"id,attr"`
}
type ElementC struct {
Text string `xml:"text,attr"`
}解析后,RootNode.Elements 是一个严格按 XML 原始顺序排列的切片,每个 Element 实例的 XMLName.Local 即为 "ElementA"、"ElementB" 等,且对应子字段(A/B/C)会自动反序列化其内部内容:
// 使用示例 data := `` var root RootNode if err := xml.Unmarshal([]byte(data), &root); err != nil { panic(err) } for i, e := range root.Elements { switch e.XMLName.Local { case "ElementA": fmt.Printf("[%d] ElementA: %s\n", i, e.A.Value) case "ElementB": fmt.Printf("[%d] ElementB (id=%s)\n", i, e.B.ID) case "ElementC": fmt.Printf("[%d] ElementC (text=%s)\n", i, e.C.Text) } } // 输出: // [0] ElementB (id=b1) // [1] ElementA: hello // [2] ElementC (text=world) hello
当面对 XSD 中