xml.Unmarshal适合解析结构固定的XML,需定义导出字段并显式声明xml tag;xml.Decoder适用于大型或动态XML,支持流式解析;命名空间和字符实体需手动处理。

用 xml.Unmarshal 解析已知结构的 XML 文件
Go 标准库的 xml 包适合处理结构固定、字段明确的 XML,比如配置文件或 API 响应。核心是定义与 XML 元素一一对应的 Go 结构体,并用 xml.Unmarshal 加载内容。
常见错误:结构体字段未导出(首字母小写),或缺少 xml tag 导致字段始终为空;XML 中的属性(attribute)未用 attr 标识,被当成子元素忽略。
- 结构体字段必须大写开头(可导出),且推荐显式声明
xmltag,例如XMLName xml.Name `xml:"book"` - 属性需用
attr,如ID string `xml:"id,attr"` - 文本内容用
chardata,如Content string `xml:",chardata"` - 嵌套元素直接嵌入结构体字段,无需额外包装
示例片段:
type Book struct {
XMLName xml.Name `xml:"book"`
ID string `xml:"id,attr"`
Title string `xml:"title"`
Author string `xml:"author"`
}
data, _ := os.ReadFile("book.xml")
var b Book
xml.Unmarshal(data, &b) // 注意传指针
用 xml.Decoder 流式解析大型或嵌套不规则 XML
当 XML 文件很大(百 MB 级)、结构动态(如混合标签、未知层级)、或只需提取部分节点时,xml.Decoder 更合适。它逐 token 解析,内存占用低,可控性强。
立即学习“go语言免费学习笔记(深入)”;
容易踩的坑:忘记调用 decoder.Token() 向前推进;对 xml.StartElement 和 xml.CharData 的顺序判断不严谨;未处理命名空间导致匹配失败。
- 用
decoder.DecodeElement(&v, &start)可将当前元素解到结构体,但需先读到对应StartElement - 用
strings.HasPrefix(token.(xml.StartElement).Name.Local, "item")处理带命名空间的标签 - 遇到
xml.CharData时记得strings.TrimSpace,否则含换行和空格 - 深层嵌套建议用栈记录路径,避免硬编码层级判断
处理 XML 命名空间和特殊字符实体
真实 XML(尤其 SOAP 或 RSS)常含命名空间(如 xmlns:dc="http://purl.org/dc/elements/1.1/")和字符实体(zuojiankuohaophpcn、 )。Go 的 xml 包默认不自动展开实体,也不校验命名空间。
关键点:命名空间前缀不影响解析,但 xml.Name.Space 字段会保留 URI;字符实体需手动替换,标准库不处理。
- 匹配带命名空间的元素时,比较
token.(xml.StartElement).Name.Space而非前缀 - 若需解码 HTML 实体,可用
html.UnescapeString(注意它也处理NNN;形式) - 写 XML 时用
xml.EscapeText防止非法字符破坏结构 - 第三方库如
github.com/knieriem/xml支持更严格的命名空间绑定,但标准库已覆盖多数场景
写 XML 时控制缩进、属性顺序与 CDATA
xml.MarshalIndent 能生成可读格式,但 Go 默认不保证属性顺序,也不支持原生 CDATA。这些细节在对接遗留系统或调试时很关键。
常见疏漏:直接拼接字符串插入 CDATA 导致二次转义;用 map 存属性导致顺序随机;缩进后空白符被误认为内容。
- CDATA 必须手动构造:在
CharData字段值前加,后加]]>,并确保该字段无其他xmltag - 属性顺序靠结构体字段声明顺序,别用
map[string]string - 缩进用
xml.MarshalIndent(v, "", " "),第二个参数是前缀(通常空),第三个是每级缩进字符串 - 若需完全控制输出(如强制单标签、自闭合),得用
xml.Encoder手动写 token
复杂 XML 场景下,结构体定义和 token 级控制往往要配合使用,纯反射方式容易在边界 case 失效。










