GolangGo 语言 XML 的序列与反序列化实践

Posted Fxtack

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GolangGo 语言 XML 的序列与反序列化实践相关的知识,希望对你有一定的参考价值。

Go 语言 XML 的序列与反序列化实践


导读

本文使用 Go 原生支持的包,对 XML 字符串以及 .xml 文件进行序列化与反序列化实践。同时对 Go 语言下的 JSON 序列化反序列化与 XML 的序列化反序列化进行性能测试与比对。

  • 使用到的包是 encoding/xml。详细文档可查看官方中文文档,链接为: https://studygolang.com/pkgdoc

  • Go 语言 JSON 的序列与反序列化博客已经更新:
    【Golang】Go 语言 JSON 的序列化与反序列化实践

  • 请留意代码块中的注释,注释中有表示出一些非常重要的细节!

  • 本文对 XML 的序列化与反序列化使用较基础,尚未对 XML 文件的标签属性等进行解析。若读者有需要,本文起到抛砖引玉的作用,详细的使用参见官方文档。



一. 案例提出

我们以图书馆为原型进行建模。对于一个图书馆用 library.xml 文件进行描述。

<?xml version="1.0"?>
<Library>
	<name>China library</name>
	<location>China</location>
	<isPrivate>false</isPrivate>
	<score>4.6</score>
	<date>
		<open>2020-07-31T14:27:10.035542+08:00</open>
		<close>2020-07-31T14:27:10.035542+08:00</close>
	</date>
	<bookInfo>
		<number>1005</number>
		<book>
			<name>Go 语言 JSON 序列化与反序列化</name>
			<author>fxtack</author>
			<publishTime>2021-07-08T17:44:53.199855+08:00</publishTime>
		</book>
		<book>
			<name>Go 语言 XML 序列化与反序列化</name>
			<author>fxtack</author>
			<publishTime>2021-07-08T17:44:53.199855+08:00</publishTime>
		</book>
	</bookInfo>
</Library>

下表对字段进行解释

字段名类型映射 Go 类型解释
library-Library(自定义结构体)图书馆根标签
name字符串string图书馆名
location字符串string所在地
isPrivate布尔bool是否是私有图书馆
score浮点数float64点评星级
date-Date(自定义结构体)图书馆开馆闭馆
open字符串time.Time开馆时间
close字符串time.Time必馆时间
bookInfo-BookInfo(自定义结构体)图书信息
number整型int图书总数
book-Book(自定义结构体)图书
(book) name字符串string书名
(book) author字符串string作者
(book) publishTime字符串time.Time发布时间

Go 语言的结构体设计如下:
(其中 library.xml 文件中的标签字段名与 Go 结构体字段中的 xml 标签值一一对应)

package main

type Library struct 
	Name      string   	`xml:"name"`
	Location  string   	`xml:"location"`
	IsPrivate bool		`xml:"isPrivate"`
	Score     float64	`xml:"score"`
	Date      Date     	`xml:"date"`
	BookInfo  BookInfo 	`xml:"bookInfo"`



type Date struct 
	Open	time.Time	`xml:"open"`
	Close 	time.Time	`xml:"close"`


type BookInfo struct 
	Number 		int		`xml:"number"`
	BookList	[]Book 	`xml:"book"`


type Book struct 
	Name 		string		`xml:"name"`
	Author		string		`xml:"author"`
	PublishTime	time.Time	`xml:"publishTime"`

基于上述案例与模型进行序列化与反序列化。


一. XML 的序列化

1. 序列化的简单示例

在进行序列化对象为 .xml 文件之前,我们先创建一个 library 对象。

package main

import "time"

var library = Library
	Name:      "一个图书馆",
	Location:  "中国",
	IsPrivate: true,
	Score:     4.9,
	Date: Date
		Open:  time.Now(),
		Close: time.Now(),
	,
	BookInfo: BookInfo
		Number: 1005,
		BookList: []Book
			
				Name: "Go 语言 JSON 序列化与反序列化",
				Author: "fxtack",
				PublishTime: time.Now(),
			,
			
				Name:        "Go 语言 XML 序列化与反序列化",
				Author:      "fxtack",
				PublishTime: time.Now(),
			,
		,
	,

通过序列化我们可以将该对象变为一个 XML 字符串,并简单的打印输出:

package main

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

func main()

	// 序列化同在 main 包下的 library
	result, err := xml.Marshal(&library)
	if err != nil 
		log.Println("对象序列化为 XML 字符串失败")
	
	// result 是字节切片类型,要转为字符串再输出
	fmt.Println(string(result))

输出结果如下。

<Library><name>广东大图书馆</name><location>广东</location><isPrivate>false</isPrivate><score>4.6</score><date><open>2021-07-08T18:26:33.7720883+08:00</open><close>2021-07-08T18:26:33.7720883+08:00</close></date><bookInfo><number>1005</number><book><name>Go 语言 JSON 序列化与反序列化</name><author>fxtack</author><publishTime>2021-07-08T18:26:33.7720883+08:00</publishTime></book><book><name>Go 语言 XML 序列化与反序列化</name><author>fxtack</author><publishTime>2021-07-08T18:26:33.7720883+08:00</publishTime></book></bookInfo></Library>

很明显这个输出结果的格式太难看了,很长一条。Go 也有考虑到这个问题,因此我们可以使用 xml.MarshalIndent 方法来解决格式化输出的问题。

package main

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

func main()
	// 使用了 xml.MarshalIndent 来进行格式序列化
	// 第二个参数为 xml 每行的前缀
	// 第三个参数为标签嵌套时使用的空位符,这里选择用缩进值,也可以使用四个空格
	result, err := xml.MarshalIndent(&library, "", "\\t")
	if err != nil 
		log.Println("对象序列化为 XML 字符串失败")
	
	fmt.Println(string(result))

此时的输出就非常规范易读了。

<Library>
	<name>广东大图书馆</name>
	<location>广东</location>
	<isPrivate>false</isPrivate>
	<score>4.6</score>
	<date>
		<open>2021-07-08T18:34:52.9834418+08:00</open>
		<close>2021-07-08T18:34:52.9834418+08:00</close>
	</date>
	<bookInfo>
		<number>1005</number>
		<book>
			<name>Go 语言 JSON 序列化与反序列化</name>
			<author>fxtack</author>
			<publishTime>2021-07-08T18:34:52.9834418+08:00</publishTime>
		</book>
		<book>
			<name>Go 语言 XML 序列化与反序列化</name>
			<author>fxtack</author>
			<publishTime>2021-07-08T18:34:52.9834418+08:00</publishTime>
		</book>
	</bookInfo>
</Library>

注意


无论是 xml.Marshal 还是 xml.MarshalIndent 方法,其传入的第一个参数值类型是 interface 类型,也就是用于进行序列化的对象。传入的可以是对象,也可以是对象的地址,以下代码的效果是相同的(以 xml.Marshal 为例),但是建议传入地址。

...
result, err := xml.Marshal(library)
...
...
result, err := xml.Marshal(&library)
...

2. 序列化的完整代码

完整的序列化过程需要对象序列化后输出为 .xml 文件。其流程如下:

构建序列化对象的结构体
由结构体创建对象
序列化得到 XML 格式字符串
写入 .xml 文件

其中,有一个重要的细节,.xml 文件开头有一个头: <?xml version="1.0"?>。这个头在序列化的结果中是没有的,所以得自己写入。

以下为完整代码:

package main

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

// library 对象同在 main 包下,详情见上文
func main() 

	// 输出为当前项目目录下的 library.xml
	WriteXML("./library.xml", &library)


func WriteXML(filePath string, ref interface) 

	// 序列化
	data, err := xml.MarshalIndent(ref, "", "\\t")
	if err != nil 
		log.Println("文件序列化错误")
	
	
	// 拼接 xml 文件头到 xml 字符串前
	data = append([]byte("<?xml version=\\"1.0\\"?>\\n"), data ...)

	// 写出文件
	err = os.WriteFile(filePath, data, 0666)
	if err != nil 
		log.Println("文件写入错误")
	

测试运行后在项目目录下出现 library.xml 并查看,发现 XML 的头也接上了,内容也没有问题。


二. XML 的反序列化

1. 反序列化的简单示例

以上文简单序列化过程中得到的字符串来进行反序列化测试。代码如下:

package main

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

func main() 

	// 准备接受反序列化的空对象
	var library Library
	// 反序列化的 xml 字符串
	xmlStr := "<?xml version=\\"1.0\\"?><Library><name>广东大图书馆</name><location>广东</location>" +
		"<isPrivate>false</isPrivate><score>4.6</score><date>" +
		"<open>2021-07-08T18:26:33.7720883+08:00</open>" +
		"<close>2021-07-08T18:26:33.7720883+08:00</close></date>" +
		"<bookInfo><number>1005</number><book><name>Go 语言 JSON 序列化与反序列化</name>" +
		"<author>fxtack</author><publishTime>2021-07-08T18:26:33.7720883+08:00</publishTime>" +
		"</book><book><name>Go 语言 XML 序列化与反序列化</name><author>fxtack</author><publishTime>" +
		"2021-07-08T18:26:33.7720883+08:00</publishTime></book></bookInfo></Library>"

	// 反序列化
	err := xml.Unmarshal([]byte(xmlStr), &library)
	if err != nil 
		log.Println("反序列化失败")
	
	fmt.Println(library)

输出如下,可见反序列化成功。

广东大图书馆 广东 false 4.6 2021-07-08 18:26:33.7720883 +0800 CST 2021-07-08 18:26:33.7720883 +0800 CST 1005 [Go 语言 JSON 序列化与反序列化 fxtack 2021-07-08 18:26:33.7720883 +0800 CST Go 语言 XML 序列化与反序列化 fxtack 2021-07-08 18:26:33.7720883 +0800 CST]

注意


反序列化的函数 xml.Unmarshal 和序列化的函数不同,其传入的第二个参数必须是用于接受结果的结构体对象的地址。即,如下代码第一段是正确的,第二段是错误的。

...
// 正确
err := xml.Unmarshal([]byte(xmlStr), &library)
...
...
// 错误,会产生 err
err := xml.Unmarshal([]byte(xmlStr), library)
...

2. 反序列化的完整代码

反序列化的完整代码要读取 .xml 文件,将读取的内容进行反序列化,得到一个对象。流程如下:

以上是关于GolangGo 语言 XML 的序列与反序列化实践的主要内容,如果未能解决你的问题,请参考以下文章

GolangGo 语言 XML 的序列与反序列化实践

GolangGo 语言 XML 的序列与反序列化实践

GolangGo 语言 JSON 的序列化与反序列化实践

GolangGo 语言 JSON 的序列化与反序列化实践

GolangGo 语言 JSON 的序列化与反序列化实践

GolangGo 语言 JSON 的序列化与反序列化实践