Golang中如何实现通用XML反序列化
Golang中如何实现通用XML反序列化 我正在创建一个系统,它必须能够解析任何数据并尝试将其映射到数据库。
我遇到的问题是如何读取通用的XML文件。 对于JSON,我能够通过以下方式实现:
var obj interface{}
err := json.Unmarshal([]byte(text), &obj)
这会返回一个包含数据的填充后的映射。但是,当我尝试对XML进行类似操作时,返回的obj中的数据是nil:
var obj interface{}
err := xml.Unmarshal([]byte(text), &obj)
如何在不知道数据结构的情况下,从XML文件中获取数据的映射?
更多关于Golang中如何实现通用XML反序列化的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
在Go中实现通用XML反序列化需要使用xml.Unmarshal配合自定义的interface{}类型处理。以下是两种实用方法:
方法1:使用map[string]interface{}解析
package main
import (
"encoding/xml"
"fmt"
"strings"
)
func parseXML(data []byte) (map[string]interface{}, error) {
var result map[string]interface{}
result = make(map[string]interface{})
decoder := xml.NewDecoder(strings.NewReader(string(data)))
for {
token, err := decoder.Token()
if err != nil {
break
}
switch se := token.(type) {
case xml.StartElement:
// 处理开始标签
var elem interface{}
if err := decoder.DecodeElement(&elem, &se); err == nil {
result[se.Name.Local] = elem
}
}
}
return result, nil
}
func main() {
xmlData := `
<person>
<name>John Doe</name>
<age>30</age>
<address>
<street>123 Main St</street>
<city>New York</city>
</address>
</person>`
result, err := parseXML([]byte(xmlData))
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("%+v\n", result)
}
方法2:使用递归解析所有节点
package main
import (
"encoding/xml"
"fmt"
"strings"
)
type Node struct {
XMLName xml.Name
Attrs []xml.Attr `xml:",any,attr"`
Content string `xml:",chardata"`
Children []Node `xml:",any"`
}
func parseToMap(data []byte) (map[string]interface{}, error) {
var node Node
if err := xml.Unmarshal(data, &node); err != nil {
return nil, err
}
return nodeToMap(node), nil
}
func nodeToMap(node Node) map[string]interface{} {
result := make(map[string]interface{})
// 添加属性
if len(node.Attrs) > 0 {
attrs := make(map[string]string)
for _, attr := range node.Attrs {
attrs[attr.Name.Local] = attr.Value
}
result["_attrs"] = attrs
}
// 添加文本内容
if node.Content != "" {
result["_content"] = strings.TrimSpace(node.Content)
}
// 处理子节点
if len(node.Children) > 0 {
childMap := make(map[string]interface{})
for _, child := range node.Children {
childName := child.XMLName.Local
childData := nodeToMap(child)
// 处理重复元素(转换为数组)
if existing, exists := childMap[childName]; exists {
switch v := existing.(type) {
case []interface{}:
childMap[childName] = append(v, childData)
default:
childMap[childName] = []interface{}{v, childData}
}
} else {
childMap[childName] = childData
}
}
result["_children"] = childMap
}
return result
}
func main() {
xmlData := `
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J.K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>`
result, err := parseToMap([]byte(xmlData))
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("%+v\n", result)
}
方法3:使用第三方库(如etree)
package main
import (
"fmt"
"github.com/beevik/etree"
)
func parseWithEtree(xmlData string) map[string]interface{} {
doc := etree.NewDocument()
if err := doc.ReadFromString(xmlData); err != nil {
return nil
}
root := doc.Root()
return parseElement(root)
}
func parseElement(elem *etree.Element) map[string]interface{} {
result := make(map[string]interface{})
// 添加属性
if len(elem.Attr) > 0 {
attrs := make(map[string]string)
for _, attr := range elem.Attr {
attrs[attr.Key] = attr.Value
}
result["_attrs"] = attrs
}
// 添加文本
if elem.Text() != "" {
result["_content"] = elem.Text()
}
// 处理子元素
children := elem.ChildElements()
if len(children) > 0 {
childMap := make(map[string]interface{})
for _, child := range children {
childName := child.Tag
childData := parseElement(child)
if existing, exists := childMap[childName]; exists {
switch v := existing.(type) {
case []interface{}:
childMap[childName] = append(v, childData)
default:
childMap[childName] = []interface{}{v, childData}
}
} else {
childMap[childName] = childData
}
}
result["_children"] = childMap
}
return result
}
func main() {
xmlData := `<user id="123">
<name>Alice</name>
<email>alice@example.com</email>
<roles>
<role>admin</role>
<role>user</role>
</roles>
</user>`
result := parseWithEtree(xmlData)
fmt.Printf("%+v\n", result)
}
这些方法提供了不同层次的通用XML解析能力。方法2的递归解析可以处理任意深度的嵌套结构,方法3使用etree库提供了更完整的XML处理功能。根据你的具体需求选择合适的实现方式。


