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 回复

谢谢查尔斯,我会看一下

更多关于Golang中如何实现通用XML反序列化的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


GitHub

clbanning/mxj

解码/编码 XML 到/从 map[string]interface{}(或 JSON);使用点符号路径和通配符提取值。替代 x2j 和 j2x 包。 - clbanning/mxj

在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处理功能。根据你的具体需求选择合适的实现方式。

回到顶部