内容简介:XML是Web服务中常用的数据通信格式。今天,它在Web开发中扮演着越来越重要的角色。本次分享将介绍如何通过Go的标准库使用XML。我不会尝试教授XML的语法或约定。你可以去阅读有关XML本身的更多文档。我只关注如何在Go中编码和解码XML文件。假设您在IT中工作,并且您必须处理以下XML配置文件:上面的XML文档包含有关服务器的两种信息:服务器名称和IP。我们将在以下示例中使用此文档。如何解析这个XML文档?可以使用Go的xml包中的Unmarshal函数来执行此操作。
XML
XML是Web服务中常用的数据通信格式。今天,它在Web开发中扮演着越来越重要的角色。本次分享将介绍如何通过 Go 的标准库使用XML。我不会尝试教授XML的语法或约定。你可以去阅读有关XML本身的更多文档。我只关注如何在Go中编码和解码XML文件。假设您在IT中工作,并且您必须处理以下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文档包含有关服务器的两种信息:服务器名称和IP。我们将在以下示例中使用此文档。
解析XML
如何解析这个XML文档?可以使用Go的xml包中的Unmarshal函数来执行此操作。
func Unmarshal(data []byte, v interface{}) error
data参数从XML源接收数据流,v是要将解析后的XML输出到的结构中。它是一个接口,这意味着您可以将XML转换为您想要的任何结构。在这里,我们只讨论如何从XML转换为结构类型,因为它们共享相似的树结构。
示例代码:
package main import ( "encoding/xml" "fmt" "io/ioutil" "os" ) type Server struct { XMLName xml.Name `xml:"server"` ServerName string `xml:"serverName"` ServerIP string `xml:"serverIP"` } type XMLServers struct { XMLName xml.Name `xml:"servers"` Version string `xml:"version,attr"` Servers []Server `xml:"server"` Description string `xml:",innerxml"` } func main() { file, err := os.Open(config.TemplateDir + "/server.xml") if err != nil { fmt.Println(err) return } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { fmt.Println(err) return } v := XMLServers{} err = xml.Unmarshal(data, &v) if err != nil { fmt.Println(err) return } fmt.Fprintln(w, 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.Unmarshal将XML文档解析为相应的struct对象。您应该看到我们的结构中有xml:"serverName"。这是结构体的一个特征,称为结构标记,用于帮助反射。让我们再看一下Unmarshal的定义:
func Unmarshal(data []byte, v interface{}) error
第一个参数是XML数据流。第二个参数是存储类型,支持struct,slice和string类型。Go的XML包使用反射进行数据映射,因此应该导出v中的所有字段。但是,这会导致一个问题:它如何知道哪个XML字段对应于映射的struct字段?答案是XML解析器按特定顺序解析数据。库将首先尝试查找匹配的struct标记。如果找不到匹配,则搜索结构字段名称。请注意,所有标记,字段名称和XML元素都区分大小写,因此您必须确保映射成功时存在一对一的对应关系。Go的反射机制允许您使用此标记信息将XML数据反映到结构对象。如果您想了解有关Go中反射的更多信息,请阅读有关struct标签和反射的包文档。
使用xml包将XML文档解析为结构时,以下是一些规则:
- 如果字段类型是带有标签",innerxml"的字符串或[]字节,则Unmarshal将为其分配原始XML数据,如上例中的描述:Shanghai_VPN127.0.0.1Beijing_VPN
- 如果一个字段被称为XMLName并且其类型是xml.Name,那么它将获取元素名称,就像上面示例中的servers一样。
- 如果字段的标记包含相应的元素名称,那么它也会获取元素名称,如上例中的servername和serverip。
- 如果字段的标记包含",attr",则它获取相应元素的属性,如上例中的版本。
- 如果字段的标记包含类似"a>b>c"的内容,则它获取节点a的节点b的元素c的值。
- 如果字段的标记包含"=",那么它什么也得不到。
- 如果字段的标记包含",any",则它将获取所有不符合其他规则的子元素。
- 如果XML元素有一个或多个注释,则所有这些注释都将添加到包含",comments"的标记的第一个字段中。该字段类型可以是string或[]byte。如果此类字段不存在,则丢弃所有注释。
这些规则告诉您如何在结构中定义标记。一旦理解了这些规则,将XML映射到结构就像上面的示例代码一样简单。因为标签和XML元素具有一对一的对应关系,所以我们也可以使用切片来表示同一级别上的多个元素。请注意,结构中的所有字段都应导出(大写),以便正确解析数据。
生成XML
如果我们想要生成XML文档而不是解析XML文档,该怎么办?我们如何在Go中做到这一点?xml包提供了两个函数,即Marshal和MarshalIndent,其中第二个函数自动缩进编组的XML文档。
他们的定义如下:
func Marshal(v interface{}) ([]byte, error) func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
这两个函数中的第一个参数用于存储编组的XML数据流。让我们看一个例子,看看它是如何工作的:
package main import ( "encoding/xml" "fmt" "os" ) type Server struct { XMLName xml.Name `xml:"server"` ServerName string `xml:"serverName"` ServerIP string `xml:"serverIP"` } type XMLServers struct { XMLName xml.Name `xml:"servers"` Version string `xml:"version,attr"` Servers []Server `xml:"server"` Description string `xml:",innerxml"` } func main() { v := &XMLServers{ Version: "1", } v.Servers = append(v.Servers, Server{ ServerName: "Shanghai_VPN", ServerIP: "127.0.0.1", }) v.Servers = append(v.Servers, Server{ ServerName: "Beijing_VPN", ServerIP: "127.0.0.2", }) output, err := xml.MarshalIndent(v, "", " ") if err != nil { fmt.Println(err) return } os.Stdout.Write([]byte(xml.Header)) os.Stdout.Write(output) }
上面的示例打印以下信息:
<?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>
正如我们之前定义的那样,我们有os.Stdout.Write([] byte(xml.Header))的原因是因为xml.MarshalIndent和xml.Marshal都没有自己输出XML头,所以我们必须明确
打印它们以便正确生成XML文档。在这里我们可以看到Marshal还接收了一个类型为interface {}的v参数。那么编组XML文档时的规则是什么?
- 如果v是数组或切片,则它会打印所有元素,如value。
- 如果v是指针,则它打印v指向的内容,当v为nil时不打印任何内容。
- 如果v是一个接口,它也会处理接口。
- 如果v是其他类型之一,则打印该类型的值。
那么xml.Marshal如何决定元素的名称?它遵循随后的规则:
- 如果v是结构,则它在XMLName的标记中定义名称。
- 字段名称为XMLName,类型为xml.Name。
- struct中的字段标记。
- struct中的字段名称。
- 输入编组名称。
然后我们需要弄清楚如何设置标记以生成最终的XML文档。
- 不会打印XMLName。
- 包含"-"标签的字段将不会被打印。
- 如果标记包含"name,attr",则它使用name作为属性名称,使用字段值作为值,就像上面示例中的版本一样。
- 如果标记包含",attr",则它使用字段的名称作为属性名称,将字段值作为其值。
- 如果标签包含",chardata",则它会打印字符数据而不是元素。
- 如果标签包含",innerxml",则打印原始值。
- 如果标记包含",comment",则会将其作为注释打印而不进行转义,因此您的值不能包含"-"。
- 如果标记包含"omitempty",则如果其值为零值,则忽略该字段,包括false,0,nil指针或nil接口,数组的长度为零,slice,map和string。
- 如果标签包含"a>b>c",则打印三个元素,其中a包含b,b包含c
以上所述就是小编给大家介绍的《Go基础学习记录之如何解析并生成XML》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Java生成、解析二维码
- Python生成器以及应用实例解析
- Django源码解析|Migrations文件的生成
- golang生成JSON及解析JSON
- Python 迭代器、生成器和列表解析
- Go基础学习记录之如何解析并生成JSON
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Never Lost Again
[美] Bill Kilday / HarperBusiness / 2018-5-29 / USD 8.00
As enlightening as The Facebook Effect, Elon Musk, and Chaos Monkeys—the compelling, behind-the-scenes story of the creation of one of the most essential applications ever devised, and the rag-tag tea......一起来看看 《Never Lost Again》 这本书的介绍吧!