
本文详解 Go encoding/xml 包中嵌套同名标签(如多个 )的正确反序列化方法,避免因结构体定义不当导致仅获取最后一个值的问题。
本文详解 go encoding/xml 包中嵌套同名标签(如多个
在 Go 中使用 xml.Unmarshal 解析 XML 时,若目标字段为切片类型(如 []string),必须通过精确的 XML 路径标签指定其直接父元素下的重复子节点,而非中间封装结构体。原代码中定义了 Names []Names 并试图通过 xml:"names" 绑定,但 Names 结构体自身又含 Name string xml:"name" —— 这种嵌套结构会导致 xml 包将每个 <names></names> 元素单独解码为一个 Names 实例,而由于 <names></names> 在 XML 中仅出现一次(每个 <member></member> 下一个),最终只保留最后一次解码结果,造成“仅返回最后一个 <name></name> 值”的错觉。
正确的做法是跳过冗余结构体,直接映射到 []string 并指定完整路径:
type Member struct {
Names []string `xml:"names>name"` // 直接提取所有 <names> 下的 <name> 文本内容
Value string `xml:"value>string"`
}xml:"names>name" 表示:在当前 <member></member> 节点下,查找 <names></names> 子节点,再从中提取全部 <name></name> 子节点的文本内容,并自动聚合为 []string。Go 的 XML 解码器会自动识别该字段为切片,并收集所有匹配节点的值。
完整可运行示例如下:
package main
import (
"encoding/xml"
"fmt"
)
type Member struct {
Names []string `xml:"names>name"`
Value string `xml:"value>string"`
}
type Result struct {
Members []Member `xml:"params>param>value>array>data>value>struct>member"`
}
func main() {
data := `<methodResponse>
<params>
<param>
<value>
<array>
<data>
<value>
<string>12345abcde12345abcde12345</string>
</value>
<value>
<struct>
<member>
<names>
<name> 2 </name>
<name> 3 </name>
<name> 4 </name>
</names>
<value>
<string>trex</string>
</value>
</member>
<member>
<names>
<name> 2 </name>
<name> 3 </name>
<name> 4 </name>
</names>
<value>
<string>trex</string>
</value>
</member>
</struct>
</value>
</data>
</array>
</value>
</param>
</params>
</methodResponse>`
v := Result{}
if err := xml.Unmarshal([]byte(data), &v); err != nil {
panic(err)
}
fmt.Printf("%+v\n", v)
// 输出:{Members:[{Names:[ 2 3 4 ] Value:trex} {Names:[ 2 3 4 ] Value:trex}]}
}✅ 关键要点总结:
- 切片字段(如
[]string)必须配合xml:"parent>child"形式路径,直接指向重复出现的叶子节点; - 避免为简单文本节点创建中间结构体(如
Names),除非需额外属性(如xml:"name,attr"); - 空格会被保留在字符串中(如
" 2 "),如需清理可后续调用strings.TrimSpace; - 若 XML 中存在命名空间,需在 tag 中显式声明(如
xml:"ns:name"),并确保xml.Decoder已注册前缀。
遵循此模式,即可稳定、简洁地解析任意数量的同级同名 XML 元素。

















