Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > golang xml文件解析

golang进行xml文件解析的操作方法

作者:辛巴克

本文介绍了Go语言中解析XML文件的几种方法:小文件解析、大文件流式解析和复杂结构解析,对于小文件,使用标准库中的encoding/xml包;对于大文件,采用流式解析以避免内存溢出,对于复杂结构的XML文件,推荐使用第三方库github.com/beevik/etree

查阅了些资料自己记录一下

一、小文件简单解析

对于小文件的 XML 解析,我们可以使用 Go 标准库中的encoding/xml包来实现。

假设我们有一个名为demo.xml的文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<config>
   <smtpServer>smtp.163.com</smtpServer>
   <smtpPort>25</smtpPort>
   <sender>user@163.com</sender>
   <senderPasswd>123456</senderPasswd>
   <receivers flag="true">
     <age>16</age>
     <user>Mike_Zhang@live.com</user>
     <user>test1@qq.com</user>
     <script>
     <![CDATA[
        function matchwo(a,b) {
            if (a < b && a < 0) then {
                return 1;
            } else {
                return 0;
            }
        }
        ]]>
     </script>
  </receivers>
 </config>

对应的main.go文件代码如下:

package main
import (
	"fmt"
	"io/ioutil"
	"encoding/xml"
)
// 定义结构体来映射 XML 结构
type SConfig struct {
	XMLName    xml.Name `xml:"config"`
	SmtpServer string   `xml:"smtpServer"`
	SmtpPort   int      `xml:"smtpPort"`
	Sender     string   `xml:"sender"`
	SenderPasswd string   `xml:"senderPasswd"`
	Receivers  SReceivers `xml:"receivers"`
}
type SReceivers struct {
	Age    int      `xml:"age"`
	Flag   string   `xml:"flag,attr"`
	User   []string `xml:"user"`
	Script string   `xml:"script"`
}
func readXml(path string) {
	// 直接读取文件内容,ioutil 内部处理打开和关闭操作
	data, err := ioutil.ReadFile(path)
	if err!= nil {
		fmt.Println("读文件出错!", err)
		return
	}
	// 初始化结构体变量
	v := SConfig{}
	err = xml.Unmarshal(data, &v)
	if err!= nil {
		fmt.Printf("error: %v", err)
		return
	}
	// 打印解析后的结果
	fmt.Println("SmtpServer : ", v.SmtpServer)
	fmt.Println("SmtpPort : ", v.SmtpPort)
	fmt.Println("Sender : ", v.Sender)
	fmt.Println("SenderPasswd : ", v.SenderPasswd)
	fmt.Println("Receivers.Flag : ", v.Receivers.Flag)
	fmt.Println("Receivers.Age : ", v.Receivers.Age)
	fmt.Println("Receivers.Script : ", v.Receivers.Script)
	for i, element := range v.Receivers.User {
		fmt.Println(i, element)
	}
}
func main() {
	readXml("demo.xml")
}

运行这段代码后,输出如下:

SmtpServer :  smtp.163.com
SmtpPort :  25
Sender :  user@163.com
SenderPasswd :  123456
Receivers.Flag :  true
Receivers.Age :  16
Receivers.Script :

        function matchwo(a,b) {
            if (a < b && a < 0) then {
                return 1;
            } else {
                return 0;
            }
        }

0 Mike_Zhang@live.com
1 test1@qq.com

二、大文件解析

当处理较大的 XML 文件时,我们可以采用流式解析的方式,以避免一次性将整个文件加载到内存中。

同样以demo.xml文件为例,内容不变。

main.go文件代码如下:

package main
import (
	"fmt"
	"encoding/xml"
	"bufio"
	"os"
	"io"
)
// 定义结构体来映射 XML 结构
type SConfig struct {
	XMLName    xml.Name `xml:"config"`
	SmtpServer string   `xml:"smtpServer"`
	SmtpPort   int      `xml:"smtpPort"`
	Sender     string   `xml:"sender"`
	SenderPasswd string   `xml:"senderPasswd"`
	Receivers  SReceivers `xml:"receivers"`
}
type SReceivers struct {
	Age    int      `xml:"age"`
	Flag   string   `xml:"flag,attr"`
	User   []string `xml:"user"`
	Script string   `xml:"script"`
}
func readXml(path string) {
	// 打开文件
	file, errOpen := os.Open(path)
	if errOpen!= nil {
		fmt.Println("打开文件异常!", errOpen)
		return
	}
	defer file.Close()
	// 创建带缓存的 Reader
	reader := bufio.NewReader(file)
	decoder := xml.NewDecoder(reader)
	for t, err := decoder.Token(); err == nil || err == io.EOF; t, err = decoder.Token() {
		switch token := t.(type) {
		case xml.StartElement:
			name := token.Name.Local
			fmt.Println(name)
			if name == "config" {
				// 解析 config 
				var sConfig = SConfig{}
				configErr := decoder.DecodeElement(&sConfig, &token)
				if configErr!= nil {
					fmt.Println("解析错误:")
					fmt.Println(configErr)
				} else {
					fmt.Println(sConfig)
				}
				return
			}
		}
	}
}
func main() {
	readXml("demo.xml")
}

输出结果为:

config
{{ config} smtp.163.com 25 user@163.com 123456 {16 true [Mike_Zhang@live.com test1@qq.com]

        function matchwo(a,b) {
            if (a < b && a < 0) then {
                return 1;
            } else {
                return 0;
            }
        }

     }}

三、复杂结构解析

对于复杂结构的 XML 文件,我们可以使用第三方库github.com/beevik/etree来进行解析。

假设我们有一个名为bookstores.xml的文件,内容如下:

<bookstore xmlns:p="urn:schemas-books-com:prices">
  <book category="COOKING">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <p:price>30.00</p:price>
  </book>
  <book category="CHILDREN">
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <year>2005</year>
    <p:price>29.99</p:price>
  </book>
  <book category="WEB">
    <title lang="en">XQuery Kick Start</title>
    <author>James McGovern</author>
    <author>Per Bothner</author>
    <author>Kurt Cagle</author>
    <author>James Linn</author>
    <author>Vaidyanathan Nagarajan</author>
    <year>2003</year>
    <p:price>49.99</p:price>
  </book>
  <book category="WEB">
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <p:price>39.95</p:price>
  </book>
</bookstore>

main.go文件代码如下:

package main
import (
	"fmt"
	"github.com/beevik/etree"
)
func readXml(path string) {
	doc := etree.NewDocument()
	if err := doc.ReadFromFile(path); err!= nil {
		panic(err)
	}
	root := doc.SelectElement("bookstore")
	fmt.Println("ROOT element:", root.Tag)
	for _, book := range root.SelectElements("book") {
		fmt.Println("CHILD element:", book.Tag)
		if title := book.SelectElement("title"); title!= nil {
			lang := title.SelectAttrValue("lang", "unknown")
			fmt.Printf("  TITLE: %s (%s)\n", title.Text(), lang)
		}
		for _, attr := range book.Attr {
			fmt.Printf("  ATTR: %s=%s\n", attr.Key, attr.Value)
		}
	}
}
func main() {
	readXml("bookstores.xml")
}

输出结果为:

ROOT element: bookstore
CHILD element: book
  TITLE: Everyday Italian (en)
  ATTR: category=COOKING
CHILD element: book
  TITLE: Harry Potter (en)
  ATTR: category=CHILDREN
CHILD element: book
  TITLE: XQuery Kick Start (en)
  ATTR: category=WEB
CHILD element: book
  TITLE: Learning XML (en)
  ATTR: category=WEB

使用流数据进行解析xml文件如果所包含标签存在会有字段读取丢失清空这种情况进行如下编写

package main
import (
    "encoding/xml"
    "fmt"
    "io"
    "os"
)
// 定义结构体来映射HTML片段中的pre标签
type HTMLPre struct {
    XMLName xml.Name `xml:"htmlpre"`
    Text    string   `xml:",innerxml"`
}
// 定义结构体来映射Rule元素
type XCCDFRule struct {
    XMLName    xml.Name `xml:"Rule"`
    ID         string   `xml:"id,attr"`
    Title      string   `xml:"title"`
    Description string   `xml:"description"`
    HTMLPre    []HTMLPre `xml:"htmlpre"`
}
func parseRule(reader io.Reader) (*XCCDFRule, error) {
    decoder := xml.NewDecoder(reader)
    decoder.Strict = false
    decoder.AutoClose = xml.HTMLAutoClose
    decoder.Entity = xml.HTMLEntity
    var rule XCCDFRule
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        } else if err!= nil {
            return nil, err
        }
        switch se := token.(type) {
        case xml.StartElement:
            if se.Name.Local == "Rule" {
                if err := decoder.DecodeElement(&rule, &se); err!= nil {
                    return nil, err
                }
                // 进一步解析description中的HTML内容
                if err!= nil {
                    return nil, err
                }
                return &rule, nil
            }
        }
    }
    return nil, fmt.Errorf("Rule element not found")
}
func main() {
    file, err := os.Open("your_file.xml")
    if err!= nil {
        fmt.Println("打开文件错误:", err)
        return
    }
    defer file.Close()
    rule, err := parseRule(file)
    if err!= nil {
        fmt.Println("解析错误:", err)
        return
    }
    fmt.Printf("规则ID: %s\n", rule.ID)
    fmt.Printf("规则标题: %s\n", rule.Title)
    fmt.Printf("规则描述: %s\n", rule.Description)
}

在这个改进后的代码中:

请注意,这只是一种处理方式,根据你的实际需求,可能需要进一步调整和扩展代码来处理XML中更复杂的HTML嵌套结构或其他类型的内容。同时,将"your_file.xml"替换为实际的XML文件路径。

到此这篇关于golang进行xml文件解析的文章就介绍到这了,更多相关golang xml文件解析内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文