如何将XML反序列化为Go语言的空接口或map[string]any?
Go中XML反序列化为any或map[string]any的实现方法
标准库encoding/xml本身不支持直接将XML反序列化为any或map[string]any——这是因为XML的树形结构(包含标签名、属性、文本内容等)比JSON的键值对结构更复杂,标准库无法自动推断映射规则。但可以通过自定义递归结构体的方式实现类似效果,把XML解析为层级化的map结构。
实现方案:自定义递归节点结构体
定义一个包含XML标签名、属性、文本内容和子节点的结构体,利用xml:",any"标签捕获所有未知子元素,再将结构体递归转换为map[string]any。
package main import ( "encoding/xml" "fmt" ) // Node 通用XML节点结构,用于捕获所有元素、属性和文本 type Node struct { XMLName xml.Name Attrs []xml.Attr `xml:"-"` // 捕获所有属性 Content string `xml:",chardata"` // 捕获节点文本内容 Nodes []Node `xml:",any"` // 捕获所有子节点 } // ToMap 将Node转换为map[string]any结构 func (n Node) ToMap() any { // 如果是纯文本节点,直接返回文本内容 if len(n.Nodes) == 0 && n.Content != "" { return n.Content } m := make(map[string]any) // 处理属性,存入@attrs字段 if len(n.Attrs) > 0 { attrs := make(map[string]string) for _, attr := range n.Attrs { attrs[attr.Name.Local] = attr.Value } m["@attrs"] = attrs } // 处理子节点,按标签名分组 childMap := make(map[string][]any) for _, node := range n.Nodes { childMap[node.XMLName.Local] = append(childMap[node.XMLName.Local], node.ToMap()) } // 子节点如果只有一个,直接存单个值;多个则存数组 for tag, children := range childMap { if len(children) == 1 { m[tag] = children[0] } else { m[tag] = children } } return m } func main() { rawXML := []byte(` <regionalRating> <rating>TV-PG</rating> <region>CA</region> </regionalRating> `) var root Node if err := xml.Unmarshal(rawXML, &root); err != nil { panic(err) } // 转换为map[string]any并打印 result := root.ToMap().(map[string]any) fmt.Printf("%#v\n", result) // 输出:map[string]any{"rating":"TV-PG", "region":"CA"} }
补充说明
- 上述实现会保留XML的属性(存入
@attrs字段)、文本内容和层级结构,同时自动处理单个/多个同名子节点的情况。 - 如果不需要保留XML属性或特殊结构,也可以先将XML转换为JSON(自定义简单转换逻辑),再反序列化为
map[string]any,但这种方式会丢失XML特有的信息,仅适合简单场景。
内容的提问来源于stack exchange,提问作者user21198592
相关产品推荐
相关产品推荐

