
本文详解如何在 go 中使用 `encoding/xml` 正确反序列化含重复子元素(如多个 `
在 Go 中解析 XML 时,一个常见误区是将包含多个同名子元素的容器(如
✅ 正确建模原则
-
重复元素 → 切片字段:每个
是独立项,Tmps 应持有一个 []Tmp 切片,而非单个 Tmp。 - 字段必须导出(首字母大写):Go 的 xml 包只能访问导出字段(如 ID 而非 id),否则属性和内容均无法绑定。
-
XML 标签名默认匹配字段名:若 XML 元素名为
,对应结构体字段应命名为 AO(大写),并用 xml:"AO" 显式声明更安全;若省略标签,包会按字段名自动匹配(但要求大小写完全一致)。 - 属性名需显式标注:使用 xml:"attrName,attr" 绑定属性,如 ID stringxml:"id,attr``。
✅ 修正后的结构体定义
type Clock struct {
Date string `xml:"Date"`
Time string `xml:"Time"`
Day string `xml:"Day"`
}
type Tmp struct {
ID string `xml:"id,attr"` // 属性:id="1"
Low string `xml:"lo,attr"` // 注意原 XML 是 "lo"/"hi",非 "low"/"high"
High string `xml:"hi,attr"`
Value string `xml:",chardata"` // 文本内容:283
}
type Tmps struct {
Tmp []Tmp `xml:"Tmp"` // ✅ 切片 + 显式标签,支持多个
}
type AO struct {
ID string `xml:"id,attr"`
Val string `xml:",chardata"` // 字段名建议语义化(避免与类型名冲突)
}
type AOs struct {
AO []AO `xml:"AO"`
}
type AI struct {
ID string `xml:"id,attr"`
Low string `xml:"lo,attr"`
High string `xml:"hi,attr"`
Val string `xml:",chardata"`
}
type AIs struct {
AI []AI `xml:"AI"`
}
type WebbrickStatus struct {
XMLName xml.Name `xml:"WebbrickStatus"` // 可选:明确根元素
Ver string `xml:"Ver,attr"` // 解析版本属性
Error string `xml:"Error"`
Context string `xml:"Context"`
LoginState string `xml:"LoginState"`
DI string `xml:"DI"`
DO string `xml:"DO"`
Clock Clock `xml:"Clock"`
OWbus string `xml:"OWBus"` // 注意:XML 中为 ,非
Tmps Tmps `xml:"Tmps"`
AOS AOs `xml:"AOs"`
AIS AIs `xml:"AIs"`
} ⚠️ 关键修正说明:Tmps.Tmp、AOs.AO、AIs.AI 均改为 []T/[]AO/[]AI 切片;所有字段首字母大写(ID, Low, Val 等),确保可导出;属性名严格对应 XML 实际值:lo / hi(非 low/high),OWBus(非 OWbus);xml:",chardata" 正确捕获元素文本内容(如 283 中的 283)。
✅ 完整解码流程(含编码转换)
func GetWBStatus() bool {
strMsg := `
`
msg := []byte(strMsg)
reader := bytes.NewReader(msg)
decoder := xml.NewDecoder(reader)
decoder.CharsetReader = charset.NewReader // 处理 ISO-8859-1 → UTF-8
var wbs WebbrickStatus
if err := decoder.Decode(&wbs); err != nil {
fmt.Printf("XML decode error: %v\n", err)
return false
}
fmt.Printf("Parsed %d Tmps, %d AOs, %d AIs\n",
len(wbs.Tmps.Tmp), len(wbs.AOS.AO), len(wbs.AIS.AI))
// 示例输出:Parsed 5 Tmps, 4 AOs, 4 AIs
for i, t := range wbs.Tmps.Tmp {
fmt.Printf("Tmp[%d]: ID=%s, Low=%s, High=%s, Value=%s\n",
i, t.ID, t.Low, t.High, t.Value)
}
return true
}? 总结与最佳实践
- 永远用切片承载重复元素:这是解决“只取最后一个”的最核心修复;
- 导出 + 标签双保险:字段大写 + xml:"..." 显式声明,避免隐式匹配失败;
- 校验 XML 实际结构:用浏览器或 xmllint 查看原始响应,确认大小写、属性名(lo vs low)、元素名(OWBus);
- Charset 转换需早于解码:decoder.CharsetReader 必须在 Decode() 前设置;
- 调试技巧:先用 xml.Unmarshal([]byte(xmlStr), &v) 简化测试,排除 Reader 层干扰。
遵循以上模式,即可稳健解析各类嵌套、重复、带属性的工业设备 XML 数据,无需第三方库,纯标准库高效可靠。










