Golang实现全局XML到Map或JSON的解析器

Golang实现全局XML到Map或JSON的解析器 是否存在一个全局或标准的 XML 转映射或 JSON 转换库,还是我必须为要解析的每个 XML 部分定义结构体?

目标是对任何 XML 文档或输出,将其渲染或转换为键值对的纯文本输出。

3 回复

谢谢,让我看看。

更多关于Golang实现全局XML到Map或JSON的解析器的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我不太清楚您所说的“全局”或“标准”具体指什么,但有不少人使用这个:

GitHub

clbanning/mxj

项目预览图

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

在Go中处理任意XML到Map或JSON的转换,确实有成熟的解决方案。你可以使用encoding/xml包配合map[string]interface{}来实现通用解析,以下是两种实用方法:

方法一:使用xml.Decoder递归解析

package main

import (
    "encoding/xml"
    "fmt"
    "strings"
)

func parseXMLToMap(decoder *xml.Decoder) (map[string]interface{}, error) {
    result := make(map[string]interface{})
    
    for {
        token, err := decoder.Token()
        if err != nil {
            return result, err
        }
        
        switch se := token.(type) {
        case xml.StartElement:
            // 处理嵌套元素
            nestedMap, err := parseXMLToMap(decoder)
            if err != nil {
                return result, err
            }
            
            // 处理重复元素(转为数组)
            if existing, ok := result[se.Name.Local]; ok {
                switch val := existing.(type) {
                case []interface{}:
                    result[se.Name.Local] = append(val, nestedMap)
                default:
                    result[se.Name.Local] = []interface{}{val, nestedMap}
                }
            } else {
                result[se.Name.Local] = nestedMap
            }
            
        case xml.CharData:
            content := strings.TrimSpace(string(se))
            if content != "" {
                result["#text"] = content
            }
            
        case xml.EndElement:
            return result, nil
        }
    }
}

func XMLToMap(xmlData []byte) (map[string]interface{}, error) {
    decoder := xml.NewDecoder(strings.NewReader(string(xmlData)))
    return parseXMLToMap(decoder)
}

方法二:使用第三方库github.com/clbanning/mxj

这是更完整的解决方案,专门处理任意XML到Map的转换:

package main

import (
    "encoding/json"
    "fmt"
    "github.com/clbanning/mxj"
)

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>`

    // XML转Map
    m, err := mxj.NewMapXml([]byte(xmlData))
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Map结构: %v\n", m)
    
    // Map转JSON
    jsonData, err := m.Json()
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("JSON输出: %s\n", jsonData)
    
    // 直接XML转JSON
    jsonData2, err := mxj.XmlToJson([]byte(xmlData))
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("直接转换JSON: %s\n", jsonData2)
    
    // 处理属性
    m, _ = mxj.NewMapXml([]byte(xmlData), true) // true表示包含属性
    fmt.Printf("包含属性的Map: %v\n", m)
}

方法三:自定义通用解析器(处理属性和文本)

package main

import (
    "encoding/xml"
    "encoding/json"
    "fmt"
    "strings"
)

type Node struct {
    XMLName  xml.Name
    Attrs    []xml.Attr `xml:",any,attr"`
    Content  string     `xml:",chardata"`
    Children []*Node    `xml:",any"`
}

func (n *Node) ToMap() map[string]interface{} {
    result := make(map[string]interface{})
    
    // 添加属性
    if len(n.Attrs) > 0 {
        attrs := make(map[string]string)
        for _, attr := range n.Attrs {
            attrs[attr.Name.Local] = attr.Value
        }
        result["-attrs"] = attrs
    }
    
    // 添加文本内容
    if n.Content != "" {
        result["#text"] = strings.TrimSpace(n.Content)
    }
    
    // 处理子元素
    for _, child := range n.Children {
        childMap := child.ToMap()
        
        if existing, ok := result[child.XMLName.Local]; ok {
            switch val := existing.(type) {
            case []interface{}:
                result[child.XMLName.Local] = append(val, childMap)
            default:
                result[child.XMLName.Local] = []interface{}{val, childMap}
            }
        } else {
            result[child.XMLName.Local] = childMap
        }
    }
    
    return result
}

func main() {
    xmlStr := `
    <root attr1="value1">
        <item id="1">First</item>
        <item id="2">Second</item>
        <nested>
            <child>Content</child>
        </nested>
    </root>`
    
    var node Node
    if err := xml.Unmarshal([]byte(xmlStr), &node); err != nil {
        panic(err)
    }
    
    resultMap := node.ToMap()
    
    // 转换为JSON
    jsonData, _ := json.MarshalIndent(resultMap, "", "  ")
    fmt.Println(string(jsonData))
}

安装mxj库

go get github.com/clbanning/mxj

mxj库提供了最完整的解决方案,支持:

  • 任意XML到Map的转换
  • 属性处理
  • 命名空间支持
  • 重复元素自动转为数组
  • 直接XML到JSON转换

对于生产环境,推荐使用mxj库,它经过了充分测试且性能良好。

回到顶部