Go解析XML首选xml.Unmarshal反序列化已知结构,需导出字段+xml tag映射;动态/大文件用xml.Decoder流式处理;注意命名空间、CDATA转义、非UTF-8编码及空标签零值问题。
xml.Unmarshal 解析已知结构的 XML 文件Go 标准库的 encoding/xml 包不支持流式解析(如 SAX
),默认走的是「反序列化到 struct」路径。前提是 XML 结构稳定、可预知字段名和嵌套关系。
关键点:struct 字段必须导出(首字母大写),且需用 xml tag 显式声明映射关系,否则字段会被忽略。
xml:",chardata" 用于捕获文本内容(如 Alice 中的 Alice)xml:",attr" 用于读取属性(如 中的 id)xml:",any" 可捕获未定义子元素(但需配合自定义 UnmarshalXML 方法)type User struct {
ID int `xml:"id,attr"`
Name string `xml:"name"`
Email string `xml:"contact>email"` // 支持路径式嵌套
}
data := Bob b@x.com
var u User
err := xml.Unmarshal([]byte(data), &u) // err == nil, u.ID == 42, u.Name == "Bob"
xml.Decoder 手动遍历)当 XML 字段名不固定(如配置文件含任意插件节点)、或体积很大需边读边处理时,xml.Unmarshal 不适用,得用 xml.Decoder。
它按 token 流方式逐个读取开始标签、字符数据、结束标签等,适合条件跳过、提前终止或构建中间结构。
立即学习“go语言免费学习笔记(深入)”;
decoder.Token() 返回 xml.Token 接口,需类型断言判断是 xml.StartElement 还是 xml.CharData
xml.CharData token,需拼接decoder.Skip() 可跳过整棵子树(比如忽略注释或废弃节点)Token() 会返回具体错误,如 expected element name
decoder := xml.NewDecoder(strings.NewReader(xmlData))
for {
t, err := decoder.Token()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
switch se := t.(type) {
case xml.StartElement:
if se.Name.Local == "item" {
var item Item
decoder.DecodeElement(&item, &se) // 复用 decoder 解析当前节点
}
case xml.CharData:
// 处理纯文本内容
}
}
XML 命名空间(xmlns)会让 tag 名变成 {http://example.com}tag,直接写 xml:"tag" 会匹配失败;CDATA 内容不会被自动解码;非 UTF-8 编码(如 GBK)会导致 xml.Unmarshal 报 invalid UTF-8 错误。
xml:"{http://myns}name",或先用 decoder.NameSpace() 提取前缀再拼接xml.CharData 返回,但内部的 zuojiankuohaophpcn 等实体不会被自动转义——需手动调用 html.UnescapeString()
golang.org/x/text/encoding 转为 UTF-8,再传给 xml.NewDecoder
和 在 struct 中都映射为零值,无法区分是否显式存在xml.Unmarshal 会一次性加载全部内容进内存并反射赋值,对大文件(>100MB)易 OOM;xml.Decoder 虽流式,但没内置 XPath 或 CSS 选择器,复杂查询要自己维护栈状态。
github.com/jbowtie/gokogiri(libxml2 绑定)或 github.com/miku/gotop(轻量 SAX 风格)xml 包做了小优化,但无本质改变;仍不支持 DTD 或 XSD 验证')的样例,Go 的 XML 解析对格式敏感,松散 HTML 风格写法(如自闭合
)会直接报错最麻烦的往往不是语法,而是 XML 里混着命名空间、乱码编码、和手写的非标准空标签——这些不跑真实数据根本发现不了。