Go入门很简单:解析 XML文件的 Unmarshal 函数

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

解析 XML 文件的 Unmarshal 函数

我们来看一下 Unmarshal 的定义:

func Unmarshal(data []byte, v interface{}) error
复制代码

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

  • 结构体 struct
  • 切片 slice
  • 字符串 string

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

现在新建一个用来测试的 test.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<servers version="1">
    <server>
        <serverName>Guangzhou_node1</serverName>
        <serverIP>127.0.0.1</serverIP>
    </server>
    <server>
        <serverName>Guangzhou_node2</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} Guangzhou_node1 127.0.0.1} {{ server} Guangzhou_node2 127.0.0.2}]
<server>
    <serverName>Guangzhou_node1</serverName>
    <serverIP>127.0.0.1</serverIP>
</server>
<server>
    <serverName>Guangzhou_node2</serverName>
    <serverIP>127.0.0.2</serverIP>
</server>
}
复制代码

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

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

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

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

解析 XML 到 struct 的规则

解析 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>
复制代码
  • 如果 struct 有一个名为 XMLName 的字段名称,Unmarshal 在该字段中记录元素名称。
  • 如果 XMLName 字段具有表单的关联标记 "name""namespace-URL name",XML元素必须具有 给定的名称(以及可选的名称空间)或 Unmarshal 返回错误。
  • 如果 XML 元素具有名称与 a 匹配的属性 struct field name,其关联标记包含 ",attr""name,attr" 形式的struct field标签中的显式名称,Unmarshal 在该字段中记录属性值。
  • 如果 XML 元素的属性未由前一个处理规则和 struct 有一个字段,其中包含相关的标记 ",any,attr",Unmarshal在第一个中记录属性值这样的领域。
  • 如果 XML 元素包含字符数据,那么该数据就是累积在第一个具有标记 ",chardata" 的 struct 字段中。struct字段可以有 type [] bytestring。如果没有这样的字段,则丢弃字符数据。
  • 如果 XML 元素包含注释,则会累积它们第一个带有标记 ",comment" 的 struct 字段。结构字段可以有 type [] bytestring 。如果没有这样的话字段,comment 会被丢弃。
  • 如果 XML 元素包含名称匹配的子元素格式化为 "a" "a> b> c"的标记的前缀,unmarshal 将下降到XML 结构中寻找带有的元素给定名称,并将最内层元素映射到该结构领域。以 ">" 开头的标签相当于一个开始字段名称后跟 ">"
  • 如果 XML 元素包含名称匹配的子元素 struct 字段的 XMLName 标记,struct字段没有根据先前规则的显式名称标签,unmarshal 映射该 struct 字段的子元素。
  • 如果 XML 元素包含名称与 a 匹配的子元素没有任何模式标志的字段(",attr",",chardata" 等),Unmarshal 将子元素映射到该struct字段。
  • 如果 XML 元素包含未匹配任何子元素以上规则和 struct 有一个标记为 ",any" 的字段,unmarshal 将子元素映射到该struct字段。
  • 处理匿名结构字段,就像它的字段一样 value 是外部结构的一部分。
  • 带有标记" - " 的结构字段永远不会被 unmarshal。

总结

本文作为学习笔记,主要介绍了如何解析 XML 文件的 Unmarshal() 函数。之后再去探索更多的用法,比如在 Web 开发中读取配置文件,或者接收 XML 文件格式的文件,或者看到 XML 格式和 JSON 格式做转换。

推荐阅读:

猜你喜欢

转载自juejin.im/post/7080966375241416741
今日推荐