如何将XML反序列化为Go语言中的[]interface{}类型数组?
嘿,你遇到的这个问题确实是Go标准xml库的一个小局限——它没法自动把不同类型的XML节点直接反序列到[]interface{}数组里。不过有几个靠谱的方案可以解决,我给你详细说说:
方案1:用标准库+自定义UnmarshalXML手动处理
这是最可控的方式,不需要依赖第三方包,核心思路是给你的Root结构体实现UnmarshalXML方法,手动遍历XML的Token流,根据节点名来反序列到对应的结构体,再塞进[]interface{}数组。
举个完整的示例代码:
package main import ( "encoding/xml" "fmt" ) // 先定义你的各个结构体类型 type Button struct { XMLName xml.Name `xml:"Button"` Text string `xml:"text,attr"` } type Checkbox struct { XMLName xml.Name `xml:"Checkbox"` Checked bool `xml:"checked,attr"` } type SomeElse struct { XMLName xml.Name `xml:"Someelse"` Value int `xml:"value,attr"` } type Root struct { Content []interface{} `xml:"-"` // 用xml:"-"忽略标准库的自动解析 } // 给Root实现自定义的UnmarshalXML方法 func (r *Root) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for { token, err := d.Token() if err != nil { return err } switch elem := token.(type) { case xml.StartElement: // 根据节点名判断要反序列的结构体类型 switch elem.Name.Local { case "Button": var btn Button if err := d.DecodeElement(&btn, &elem); err != nil { return err } r.Content = append(r.Content, btn) case "Checkbox": var cb Checkbox if err := d.DecodeElement(&cb, &elem); err != nil { return err } r.Content = append(r.Content, cb) case "Someelse": var se SomeElse if err := d.DecodeElement(&se, &elem); err != nil { return err } r.Content = append(r.Content, se) } case xml.EndElement: // 遇到Root的结束节点就退出循环 if elem == start.End() { return nil } } } } func main() { xmlData := `<Root> <Button text="Submit"/> <Checkbox checked="true"/> <Someelse value="123"/> </Root>` var root Root if err := xml.Unmarshal([]byte(xmlData), &root); err != nil { panic(err) } // 遍历Content数组,用类型断言处理不同类型 for _, item := range root.Content { switch v := item.(type) { case Button: fmt.Printf("Button: %+v\n", v) case Checkbox: fmt.Printf("Checkbox: %+v\n", v) case SomeElse: fmt.Printf("SomeElse: %+v\n", v) } } }
优点:完全基于标准库,不需要额外依赖,对解析过程有绝对控制权;
缺点:新增结构体类型时,需要手动修改UnmarshalXML方法里的分支判断,适合类型数量不多的场景。
方案2:用第三方包简化动态解析
如果你的结构体类型很多,不想手动维护映射关系,可以用专门处理动态XML的第三方包,比如mxj或者etree,这里举两个常用的例子:
用mxj包(基于map的解析)
这个包可以把XML直接解析成map[string]interface{},之后你可以根据节点标签把map转成对应的结构体:
package main import ( "fmt" "github.com/clbanning/mxj" ) type Button struct { Text string `xml:"text,attr"` } type Checkbox struct { Checked bool `xml:"checked,attr"` } func main() { xmlData := `<Root> <Button text="Cancel"/> <Checkbox checked="false"/> </Root>` // 把XML解析成map结构 mv, err := mxj.NewMapXml([]byte(xmlData)) if err != nil { panic(err) } // 提取Root下的子节点 rootChildren, ok := mv["Root"].([]interface{}) if !ok { panic("invalid xml structure") } var content []interface{} for _, child := range rootChildren { childMap, ok := child.(map[string]interface{}) if !ok { continue } // 遍历map的key(就是XML节点标签) for tag, attrs := range childMap { switch tag { case "Button": attrMap := attrs.(map[string]interface{}) content = append(content, Button{Text: attrMap["text"].(string)}) case "Checkbox": attrMap := attrs.(map[string]interface{}) content = append(content, Checkbox{Checked: attrMap["checked"].(string) == "true"}) } } } // 输出结果 for _, item := range content { fmt.Printf("%+v\n", item) } }
用etree包(DOM风格解析)
这个包提供了类似HTML DOM的操作方式,可以直接遍历XML节点,然后手动提取数据转成结构体:
package main import ( "fmt" "github.com/beevik/etree" ) type Button struct { Text string } type Checkbox struct { Checked bool } func main() { xmlData := `<Root> <Button text="Click"/> <Checkbox checked="true"/> </Root>` doc := etree.NewDocument() if err := doc.ReadFromString(xmlData); err != nil { panic(err) } root := doc.SelectElement("Root") var content []interface{} // 遍历Root下的所有子元素 for _, elem := range root.ChildElements() { switch elem.Tag { case "Button": content = append(content, Button{Text: elem.SelectAttrValue("text", "")}) case "Checkbox": content = append(content, Checkbox{Checked: elem.SelectAttrValue("checked", "false") == "true"}) } } // 输出结果 for _, item := range content { fmt.Printf("%+v\n", item) } }
优点:省去了手动写UnmarshalXML的繁琐,适合类型多、XML结构多变的场景;
缺点:需要引入第三方依赖,并且从map或DOM节点转成自定义结构体时,需要做一些类型断言的处理。
总结
- 如果你的结构体类型不多,推荐用方案1,可控性强且无依赖;
- 如果类型多或者XML结构经常变化,方案2的第三方包能帮你节省不少重复代码。
内容的提问来源于stack exchange,提问作者Glebka
相关产品推荐
相关产品推荐

