糊涂世间 發表於 2020-6-8 17:41:00

go解析xml的三种方式

<h1 id="go解析xml的三种方式">go解析xml的三种方式</h1>
<p>之前项目中用到过xml解析,在这里记录一下。</p>
<h2 id="小文件简单解析">小文件简单解析</h2>
<p><code>demo.xml</code></p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;config&gt;
   &lt;smtpServer&gt;smtp.163.com&lt;/smtpServer&gt;
   &lt;smtpPort&gt;25&lt;/smtpPort&gt;
   &lt;sender&gt;user@163.com&lt;/sender&gt;
   &lt;senderPasswd&gt;123456&lt;/senderPasswd&gt;
   &lt;receivers flag="true"&gt;
   &lt;age&gt;16&lt;/age&gt;
   &lt;user&gt;Mike_Zhang@live.com&lt;/user&gt;
   &lt;user&gt;test1@qq.com&lt;/user&gt;
   &lt;script&gt;
   &lt;![CDATA[
      function matchwo(a,b) {
            if (a &lt; b &amp;&amp; a &lt; 0) then {
                return 1;
            } else {
                return 0;
            }
      }
      ]]&gt;
   &lt;/script&gt;
&lt;/receivers&gt;
&lt;/config&gt;
</code></pre>
<p><code>main.go</code></p>
<pre><code class="language-go">package main

import (
        "fmt"
        "io/ioutil"
        "encoding/xml"
)
/*
https://studygolang.com/static/pkgdoc/pkg/encoding_xml.htm
*/
// 定义结构体映射xml结构
type SConfig struct {
        XMLNamexml.Name `xml:"config"` // 指定最外层的标签为config
        SmtpServer string `xml:"smtpServer"` // 读取smtpServer配置项,并将结果保存到SmtpServer变量中
        SmtpPort int `xml:"smtpPort"`
        Sender string `xml:"sender"`
        SenderPasswd string `xml:"senderPasswd"`
        Receivers SReceivers `xml:"receivers"` // 读取receivers标签下的内容,以结构方式获取
}
   
type SReceivers struct {
        Age int `xml:"age"`
        Flag string `xml:"flag,attr"` // 读取flag属性
        User []string `xml:"user"` // 读取user数组
        Script string `xml:"script"` // 读取 &lt;!]&gt; 数据
}

func readXml(path string) {
        // 不用管理打开和关闭,ioutil 在内部已经处理过了
        data, err := ioutil.ReadFile(path)
        if err != nil {
                fmt.Println("读文件出错!", err)
                return
        }
        // fmt.Println(string(bytes))
        v := SConfig{}
        err = xml.Unmarshal(data, &amp;v)
        if err != nil {
          fmt.Printf("error: %v", err)
          return
        }
   
        //fmt.Println(v)
        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")
}
</code></pre>
<p>输出:</p>
<pre><code class="language-sh">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 &lt; b &amp;&amp; a &lt; 0) then {
                return 1;
            } else {
                return 0;
            }
      }


0 Mike_Zhang@live.com
1 test1@qq.com
</code></pre>
<p>参考博客</p>
<h2 id="大文件解析">大文件解析</h2>
<p>对于超大xml文件的读取采用事件驱动的方式节省内存提高效率:</p>
<p><code>demo.xml</code></p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;config&gt;
   &lt;smtpServer&gt;smtp.163.com&lt;/smtpServer&gt;
   &lt;smtpPort&gt;25&lt;/smtpPort&gt;
   &lt;sender&gt;user@163.com&lt;/sender&gt;
&lt;senderPasswd&gt;123456&lt;/senderPasswd&gt;
   &lt;receivers flag="true"&gt;
   &lt;age&gt;16&lt;/age&gt;
   &lt;user&gt;Mike_Zhang@live.com&lt;/user&gt;
   &lt;user&gt;test1@qq.com&lt;/user&gt;
   &lt;script&gt;
   &lt;![CDATA[
      function matchwo(a,b) {
            if (a &lt; b &amp;&amp; a &lt; 0) then {
                return 1;
            } else {
                return 0;
            }
      }
      ]]&gt;
   &lt;/script&gt;
&lt;/receivers&gt;
&lt;/config&gt;
</code></pre>
<p><code>main.go</code></p>
<pre><code class="language-go">package main

import (
        "fmt"
        "encoding/xml"
        "bufio"
        "os"
        "io"
)
/*
解析超大 xml 文件
https://studygolang.com/static/pkgdoc/pkg/encoding_xml.htm
*/
// 定义结构体映射xml结构
type SConfig struct {
        XMLNamexml.Name `xml:"config"` // 指定最外层的标签为config
        SmtpServer string `xml:"smtpServer"` // 读取smtpServer配置项,并将结果保存到SmtpServer变量中
        SmtpPort int `xml:"smtpPort"`
        Sender string `xml:"sender"`
        SenderPasswd string `xml:"senderPasswd"`
        Receivers SReceivers `xml:"receivers"` // 读取receivers标签下的内容,以结构方式获取
}
   
type SReceivers struct {
        Age int `xml:"age"`
        Flag string `xml:"flag,attr"` // 读取flag属性
        User []string `xml:"user"` // 读取user数组
        Script string `xml:"script"` // 读取 &lt;!]&gt; 数据
}

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(&amp;sConfig, &amp;token)
                                        if configErr != nil {
                                                fmt.Println("解析错误:")
                                                fmt.Println(configErr)
                                        } else {
                                                fmt.Println(sConfig)
                                        }
                                        return
                                }
                }
        }
}

func main() {
        readXml("demo.xml")
}
</code></pre>
<p>输出:</p>
<pre><code class="language-sh">config
{{ config} smtp.163.com 25 user@163.com 123456 {16 true

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

   }}
</code></pre>
<h2 id="复杂结构解析">复杂结构解析</h2>
<p>有的时候xml文件很复杂,嵌套很深,这个时候如果我们使用struct来映射就会很麻烦,好在开源了一个很方便的解析工具<code>etree</code>。这个<code>etree</code>和<code>python</code>的<code>etree</code>的<code>api</code>几乎一样,用起来简单好用。</p>
<p><code>bookstores.xml</code></p>
<pre><code class="language-xml">&lt;bookstore xmlns:p="urn:schemas-books-com:prices"&gt;

&lt;book category="COOKING"&gt;
    &lt;title lang="en"&gt;Everyday Italian&lt;/title&gt;
    &lt;author&gt;Giada De Laurentiis&lt;/author&gt;
    &lt;year&gt;2005&lt;/year&gt;
    &lt;p:price&gt;30.00&lt;/p:price&gt;
&lt;/book&gt;

&lt;book category="CHILDREN"&gt;
    &lt;title lang="en"&gt;Harry Potter&lt;/title&gt;
    &lt;author&gt;J K. Rowling&lt;/author&gt;
    &lt;year&gt;2005&lt;/year&gt;
    &lt;p:price&gt;29.99&lt;/p:price&gt;
&lt;/book&gt;

&lt;book category="WEB"&gt;
    &lt;title lang="en"&gt;XQuery Kick Start&lt;/title&gt;
    &lt;author&gt;James McGovern&lt;/author&gt;
    &lt;author&gt;Per Bothner&lt;/author&gt;
    &lt;author&gt;Kurt Cagle&lt;/author&gt;
    &lt;author&gt;James Linn&lt;/author&gt;
    &lt;author&gt;Vaidyanathan Nagarajan&lt;/author&gt;
    &lt;year&gt;2003&lt;/year&gt;
    &lt;p:price&gt;49.99&lt;/p:price&gt;
&lt;/book&gt;

&lt;book category="WEB"&gt;
    &lt;title lang="en"&gt;Learning XML&lt;/title&gt;
    &lt;author&gt;Erik T. Ray&lt;/author&gt;
    &lt;year&gt;2003&lt;/year&gt;
    &lt;p:price&gt;39.95&lt;/p:price&gt;
&lt;/book&gt;

&lt;/bookstore&gt;
</code></pre>
<p><code>main.go</code></p>
<pre><code class="language-go">package main
/*

使用 etree 解析复杂结构的 xml 文件
https://godoc.org/github.com/beevik/etree
https://pkg.go.dev/github.com/beevik/etree?tab=doc
https://github.com/beevik/etree
*/

import (
        "fmt"
        "github.com/beevik/etree"// go get 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")
}
</code></pre>
<p>输出:</p>
<pre><code class="language-sh">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
</code></pre><br><br>
来源:https://www.cnblogs.com/bartggg/p/13067153.html
頁: [1]
查看完整版本: go解析xml的三种方式