读取 XML 文件
先来看一看如何读取本地 XML 文件,同 JSON 数据类似,Go 同样需要一个结构体来接收 XML 的数据。
我们定义一个 test.xml
文件,存取的是员工信息:
<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
文件:
<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 文件。