Go 入门很简单:读取和解析 XML 文件

举报
宇宙之一粟 发表于 2022/03/30 22:49:40 2022/03/30
【摘要】 读取 XML 文件先来看一看如何读取本地 XML 文件,同 JSON 数据类似,Go 同样需要一个结构体来接收 XML 的数据。我们定义一个 ​​test.xml​​ 文件,存取的是员工信息:<?xml version="1.0" encoding="UTF-8"?><Record> <Name>Yuzhou</Name> <SurName>1su</SurName> <Tel> ...

127.png

读取 XML 文件

先来看一看如何读取本地 XML 文件,同 JSON 数据类似,Go 同样需要一个结构体来接收 XML 的数据。

我们定义一个 ​​test.xml​​ 文件,存取的是员工信息:

<?xml version="1.0" encoding="UTF-8"?>
<Record>
  <Name>Yuzhou</Name>
  <SurName>1su</SurName>
  <Tel>
    <Mobile>true</Mobile>
    <Number>12345678</Number>
  </Tel>
  <Tel>
    <Mobile>true</Mobile>
    <Number>0755-12345</Number>
  </Tel>
</Record>

然后我们看到该 XML 有一条记录,所以我们需要两个结构体:​​Record​​​ 和 ​​Telphone​​ :

type Record struct {
    Name    string
    Surname string
    Tel     []Telephone
}

type Telephone struct {
    Mobile bool
    Number string
}

然后定义一个 ​​readFromXML()​​ 函数,先读取文件,然后解析该文件:

func readFromXMl(filename string, key interface{}) error {
    in, err := os.Open(filename)
    if err != nil {
        return err
    }

    decodeXML := xml.NewDecoder(in)
    err = decodeXML.Decode(key)
    if err != nil {
        return err
    }
    in.Close()
    return nil
}

最后我们代码的完整部分如下:

package main

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

// Users结构体:所有用户组成的数组
type Record struct {
    Name    string
    Surname string
    Tel     []Telephone
}

type Telephone struct {
    Mobile bool
    Number string
}

func readFromXMl(filename string, key interface{}) error {
    in, err := os.Open(filename)
    if err != nil {
        return err
    }

    decodeXML := xml.NewDecoder(in)
    err = decodeXML.Decode(key)
    if err != nil {
        return err
    }
    in.Close()
    return nil
}

func main() {

    arguments := os.Args
    if len(arguments) == 1 {
        fmt.Println("Please provide a filename!")
        return
    }

    filename := arguments[1]

    var myRecord Record

    err := readFromXMl(filename, &myRecord)

    if err == nil {
        fmt.Println("XML:", myRecord)
    } else {
        fmt.Println(err)
    }
}

在终端执行该代码,得到如下结果:

$ go run main.go test.xml 
XML: {Yuzhou  [{true 12345678} {true 0755-12345}]}

解析 XML 文件

Go 有一个使用 ​​NewParser()​​​创建的 XML 解析器。 这需要一个 ​​io.Reader()​​​ 作为参数并返回一个指向 ​​Parser​​ 的指针。 如果 XML 文件在用户标签、嵌套元素上设置了属性,如果您能够解析这些属性,那么通过扩展,您应该能够解析任何大小的 XML 文件。

现在新建另一个 ​​test.xml​​ 文件:

<?xml version="1.0" encoding="utf-8"?>
<servers version="1">
    <server>
        <serverName>Shanghai_VPN</serverName>
        <serverIP>127.0.0.1</serverIP>
    </server>
    <server>
        <serverName>Beijing_VPN</serverName>
        <serverIP>127.0.0.2</serverIP>
    </server>
</servers>

如何解析如上这个 XML 文档呢?我们可以透过 xml 模块的 ​​Unmarshal()​​ 函数来达到我们的目的:

func Unmarshal(data []byte, v interface{}) error

data 接收的是 XML 文件流,v 是需要输出的结构,定义为 interface,也就是可以把 XML 转换为任意的格式。我们这里主要介绍 struct 的转换,因为 struct 和 XML 都有类似树状结构的特征。

定义我们的结构体:

type Recurlyservers struct {
    XMLName     xml.Name `xml:"servers"`
    Version     string   `xml:"version,attr"`
    Svs         []server `xml:"server"`
    Description string   `xml:",innerxml"`
}

type server struct {
    XMLName    xml.Name `xml:"server"`
    ServerName string   `xml:"serverName"`
    ServerIP   string   `xml:"serverIP"`
}

完整代码如下:

package main

import (
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "os"
)

type Recurlyservers struct {
    XMLName     xml.Name `xml:"servers"`
    Version     string   `xml:"version,attr"`
    Svs         []server `xml:"server"`
    Description string   `xml:",innerxml"`
}

type server struct {
    XMLName    xml.Name `xml:"server"`
    ServerName string   `xml:"serverName"`
    ServerIP   string   `xml:"serverIP"`
}

func main() {
    file, err := os.Open("test.xml") // For read access.
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    defer file.Close()
    data, err := ioutil.ReadAll(file)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    v := Recurlyservers{}
    err = xml.Unmarshal(data, &v)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }

    fmt.Println(v)
}

XML 本质上是一种树状结构,而我们可以定义与之匹配的 go 语言的结构体类型,然后通过​​xml.Unmarshal​​ 来将 XML 中的文件解析成对应的 struct 结构体。如上例子输出如下结果:

{{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}]
<server>
    <serverName>Shanghai_VPN</serverName>
    <serverIP>127.0.0.1</serverIP>
</server>
<server>
    <serverName>Beijing_VPN</serverName>
    <serverIP>127.0.0.2</serverIP>
</server>
}

上面的例子中,将 xml 文件解析成对应的 struct 结构体是通过 ​​xml.Unmarshal​​ 来完成的,

这个过程是如何实现的?可以看到我们的 struct 定义后面多了一些类似于 ​​xml:"serverName"​​ 这样的内容,这个是 struct 的一个特性,它们被称为 struct tag,它们是用来辅助反射的。我们来看一下 Unmarshal 的定义:

func Unmarshal(data []byte, v interface{}) error

我们看到函式定义了两个参数,第一个是 XML 文件流,第二个是储存的对应类型,目前支持:

  • 结构体 struct

  • 切片 slice

  • 字符串 string

XML 套件内部采用了反射来进行文件的对映,所以 v 里面的位置必须是输出的。​​Unmarshal​​ 解析的时候 XML 元素和对应类型怎么对应起来的呢?

利用一个先后顺序读取:首先会读取 struct tag,如果没有,那么就会对应栏位名。必须注意一点的是解析的时候 tag、栏位名、XML 元素都是区分大小写的的,所以必须一一对应。

解析 XML 到 struct 的时候遵循如下的规则:

  • 如果 struct 的第二个参数是 ​​string​​​ 或者 ​​[]byte​​​ 型,并且它的 tag 含有 ​​",innerxml"​​​,​​Unmarshal​​ 将会将此位置所对应的元素内所有内嵌的原始 xml 累加到此位置上,如上面例子 Description 定义。最后的输出是:

<server>
        <serverName>Shanghai_VPN</serverName>
        <serverIP>127.0.0.1</serverIP>
    </server>
    <server>
        <serverName>Beijing_VPN</serverName>
        <serverIP>127.0.0.2</serverIP>
    </server>

总结

本文主要介绍了如何读取本地 XML 文件和解析 XML 文件,分别使用 Go 的 ​encoding/ XML​​ 包中的 ​​NewDecoder()​​​ 和 ​​Unmarshal()​​ 函数。之后再去探索更多的用法,比如在 Web 开发中读取配置文件,或者接收 XML 文件格式的文件。下一篇文章将会介绍如何将 Go 数据转化为 XML 文件。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。