@@ -6,95 +6,95 @@ XML作为一种数据交换和信息传递的格式已经十分普及。而随
6
6
假如你是一名运维人员,你为你所管理的所有服务器生成了如下内容的xml的配置文件:
7
7
``` xml
8
8
9
- <?xml version =" 1.0" encoding =" utf-8" ?>
10
- <servers version =" 1" >
11
- <server >
12
- <serverName >Shanghai_VPN</serverName >
13
- <serverIP >127.0.0.1</serverIP >
14
- </server >
15
- <server >
16
- <serverName >Beijing_VPN</serverName >
17
- <serverIP >127.0.0.2</serverIP >
18
- </server >
19
- </servers >
9
+ <?xml version =" 1.0" encoding =" utf-8" ?>
10
+ <servers version =" 1" >
11
+ <server >
12
+ <serverName >Shanghai_VPN</serverName >
13
+ <serverIP >127.0.0.1</serverIP >
14
+ </server >
15
+ <server >
16
+ <serverName >Beijing_VPN</serverName >
17
+ <serverIP >127.0.0.2</serverIP >
18
+ </server >
19
+ </servers >
20
20
```
21
21
上面的XML文档描述了两个服务器的信息,包含了服务器名和服务器的IP信息,接下来的Go例子以此XML描述的信息进行操作。
22
22
23
23
## 解析XML
24
24
如何解析如上这个XML文件呢? 我们可以通过xml包的` Unmarshal ` 函数来达到我们的目的
25
25
``` Go
26
26
27
- func Unmarshal (data []byte , v interface {}) error
27
+ func Unmarshal (data []byte , v interface {}) error
28
28
```
29
29
data接收的是XML数据流,v是需要输出的结构,定义为interface,也就是可以把XML转换为任意的格式。我们这里主要介绍struct的转换,因为struct和XML都有类似树结构的特征。
30
30
31
31
示例代码如下:
32
32
```Go
33
33
34
- package main
35
-
36
- import (
37
- "encoding/xml"
38
- "fmt"
39
- "io/ioutil"
40
- "os"
41
- )
42
-
43
- type Recurlyservers struct {
44
- XMLName xml.Name ` xml:"servers"`
45
- Version string ` xml:"version,attr"`
46
- Svs []server ` xml:"server"`
47
- Description string ` xml:",innerxml"`
34
+ package main
35
+
36
+ import (
37
+ "encoding/xml"
38
+ "fmt"
39
+ "io/ioutil"
40
+ "os"
41
+ )
42
+
43
+ type Recurlyservers struct {
44
+ XMLName xml.Name ` xml:"servers"`
45
+ Version string ` xml:"version,attr"`
46
+ Svs []server ` xml:"server"`
47
+ Description string ` xml:",innerxml"`
48
+ }
49
+
50
+ type server struct {
51
+ XMLName xml.Name ` xml:"server"`
52
+ ServerName string ` xml:"serverName"`
53
+ ServerIP string ` xml:"serverIP"`
54
+ }
55
+
56
+ func main () {
57
+ file , err := os.Open (" servers.xml" ) // For read access.
58
+ if err != nil {
59
+ fmt.Printf (" error: %v " , err)
60
+ return
48
61
}
49
-
50
- type server struct {
51
- XMLName xml. Name ` xml:"server" `
52
- ServerName string ` xml:"serverName" `
53
- ServerIP string ` xml:"serverIP" `
62
+ defer file. Close ()
63
+ data , err := ioutil. ReadAll (file)
64
+ if err != nil {
65
+ fmt. Printf ( " error: %v " , err)
66
+ return
54
67
}
55
-
56
- func main () {
57
- file , err := os.Open (" servers.xml" ) // For read access.
58
- if err != nil {
59
- fmt.Printf (" error: %v " , err)
60
- return
61
- }
62
- defer file.Close ()
63
- data , err := ioutil.ReadAll (file)
64
- if err != nil {
65
- fmt.Printf (" error: %v " , err)
66
- return
67
- }
68
- v := Recurlyservers{}
69
- err = xml.Unmarshal (data, &v)
70
- if err != nil {
71
- fmt.Printf (" error: %v " , err)
72
- return
73
- }
74
-
75
- fmt.Println (v)
68
+ v := Recurlyservers{}
69
+ err = xml.Unmarshal (data, &v)
70
+ if err != nil {
71
+ fmt.Printf (" error: %v " , err)
72
+ return
76
73
}
77
74
75
+ fmt.Println (v)
76
+ }
77
+
78
78
```
79
79
XML本质上是一种树形的数据格式,而我们可以定义与之匹配的go 语言的 struct类型,然后通过xml.Unmarshal来将xml中的数据解析成对应的struct对象。如上例子输出如下数据
80
80
``` xml
81
81
82
- {{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}]
83
- <server >
84
- <serverName >Shanghai_VPN</serverName >
85
- <serverIP >127.0.0.1</serverIP >
86
- </server >
87
- <server >
88
- <serverName >Beijing_VPN</serverName >
89
- <serverIP >127.0.0.2</serverIP >
90
- </server >
91
- }
82
+ {{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}]
83
+ <server >
84
+ <serverName >Shanghai_VPN</serverName >
85
+ <serverIP >127.0.0.1</serverIP >
86
+ </server >
87
+ <server >
88
+ <serverName >Beijing_VPN</serverName >
89
+ <serverIP >127.0.0.2</serverIP >
90
+ </server >
91
+ }
92
92
93
93
```
94
94
上面的例子中,将xml文件解析成对应的struct对象是通过` xml.Unmarshal ` 来完成的,这个过程是如何实现的?可以看到我们的struct定义后面多了一些类似于` xml:"serverName" ` 这样的内容,这个是struct的一个特性,它们被称为 struct tag,它们是用来辅助反射的。我们来看一下` Unmarshal ` 的定义:
95
95
``` Go
96
96
97
- func Unmarshal (data []byte , v interface {}) error
97
+ func Unmarshal (data []byte , v interface {}) error
98
98
```
99
99
我们看到函数定义了两个参数,第一个是XML数据流,第二个是存储的对应类型,目前支持struct、slice和string,XML包内部采用了反射来进行数据的映射,所以v里面的字段必须是导出的。`Unmarshal`解析的时候XML元素和字段怎么对应起来的呢?这是有一个优先级读取流程的,首先会读取struct tag,如果没有,那么就会对应字段名。必须注意一点的是解析的时候tag、字段名、XML元素都是大小写敏感的,所以必须一一对应字段。
100
100
@@ -106,14 +106,14 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的
106
106
107
107
```xml
108
108
109
- <server>
110
- <serverName>Shanghai_VPN</serverName>
111
- <serverIP>127.0.0.1</serverIP>
112
- </server>
113
- <server>
114
- <serverName>Beijing_VPN</serverName>
115
- <serverIP>127.0.0.2</serverIP>
116
- </server>
109
+ <server>
110
+ <serverName>Shanghai_VPN</serverName>
111
+ <serverIP>127.0.0.1</serverIP>
112
+ </server>
113
+ <server>
114
+ <serverName>Beijing_VPN</serverName>
115
+ <serverIP>127.0.0.2</serverIP>
116
+ </server>
117
117
118
118
```
119
119
@@ -133,61 +133,61 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的
133
133
假若我们不是要解析如上所示的XML文件,而是生成它,那么在go语言中又该如何实现呢? xml包中提供了`Marshal`和`MarshalIndent`两个函数,来满足我们的需求。这两个函数主要的区别是第二个函数会增加前缀和缩进,函数的定义如下所示:
134
134
```Go
135
135
136
- func Marshal(v interface{}) ([]byte , error )
137
- func MarshalIndent (v interface {}, prefix , indent string ) ([]byte , error )
136
+ func Marshal(v interface{}) ([]byte , error )
137
+ func MarshalIndent (v interface {}, prefix , indent string ) ([]byte , error )
138
138
```
139
139
两个函数第一个参数是用来生成XML的结构定义类型数据,都是返回生成的XML数据流。
140
140
141
141
下面我们来看一下如何输出如上的XML:
142
142
```Go
143
143
144
- package main
145
-
146
- import (
147
- "encoding/xml"
148
- "fmt"
149
- "os"
150
- )
151
-
152
- type Servers struct {
153
- XMLName xml.Name ` xml:"servers"`
154
- Version string ` xml:"version,attr"`
155
- Svs []server ` xml:"server"`
144
+ package main
145
+
146
+ import (
147
+ "encoding/xml"
148
+ "fmt"
149
+ "os"
150
+ )
151
+
152
+ type Servers struct {
153
+ XMLName xml.Name ` xml:"servers"`
154
+ Version string ` xml:"version,attr"`
155
+ Svs []server ` xml:"server"`
156
+ }
157
+
158
+ type server struct {
159
+ ServerName string ` xml:"serverName"`
160
+ ServerIP string ` xml:"serverIP"`
161
+ }
162
+
163
+ func main () {
164
+ v := &Servers{Version: " 1" }
165
+ v.Svs = append (v.Svs , server{" Shanghai_VPN" , " 127.0.0.1" })
166
+ v.Svs = append (v.Svs , server{" Beijing_VPN" , " 127.0.0.2" })
167
+ output , err := xml.MarshalIndent (v, " " , " " )
168
+ if err != nil {
169
+ fmt.Printf (" error: %v \n " , err)
156
170
}
171
+ os.Stdout .Write ([]byte (xml.Header ))
157
172
158
- type server struct {
159
- ServerName string ` xml:"serverName"`
160
- ServerIP string ` xml:"serverIP"`
161
- }
162
-
163
- func main () {
164
- v := &Servers{Version: " 1" }
165
- v.Svs = append (v.Svs , server{" Shanghai_VPN" , " 127.0.0.1" })
166
- v.Svs = append (v.Svs , server{" Beijing_VPN" , " 127.0.0.2" })
167
- output , err := xml.MarshalIndent (v, " " , " " )
168
- if err != nil {
169
- fmt.Printf (" error: %v \n " , err)
170
- }
171
- os.Stdout .Write ([]byte (xml.Header ))
172
-
173
- os.Stdout .Write (output)
174
- }
173
+ os.Stdout .Write (output)
174
+ }
175
175
176
176
```
177
177
上面的代码输出如下信息:
178
178
``` xml
179
179
180
- <?xml version =" 1.0" encoding =" UTF-8" ?>
181
- <servers version =" 1" >
182
- <server >
183
- <serverName >Shanghai_VPN</serverName >
184
- <serverIP >127.0.0.1</serverIP >
185
- </server >
186
- <server >
187
- <serverName >Beijing_VPN</serverName >
188
- <serverIP >127.0.0.2</serverIP >
189
- </server >
190
- </servers >
180
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
181
+ <servers version =" 1" >
182
+ <server >
183
+ <serverName >Shanghai_VPN</serverName >
184
+ <serverIP >127.0.0.1</serverIP >
185
+ </server >
186
+ <server >
187
+ <serverName >Beijing_VPN</serverName >
188
+ <serverIP >127.0.0.2</serverIP >
189
+ </server >
190
+ </servers >
191
191
192
192
```
193
193
和我们之前定义的文件的格式一模一样,之所以会有` os.Stdout.Write([]byte(xml.Header)) ` 这句代码的出现,是因为` xml.MarshalIndent ` 或者` xml.Marshal ` 输出的信息都是不带XML头的,为了生成正确的xml文件,我们使用了xml包预定义的Header变量。
@@ -220,13 +220,13 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的
220
220
- tag中含有` "a>b>c" ` ,那么就会循环输出三个元素a包含b,b包含c,例如如下代码就会输出
221
221
222
222
``` xml
223
- FirstName string `xml:"name>first"`
224
- LastName string `xml:"name>last"`
223
+ FirstName string `xml:"name>first"`
224
+ LastName string `xml:"name>last"`
225
225
226
- <name >
227
- <first >Asta</first >
228
- <last >Xie</last >
229
- </name >
226
+ <name >
227
+ <first >Asta</first >
228
+ <last >Xie</last >
229
+ </name >
230
230
231
231
```
232
232
上面我们介绍了如何使用Go语言的xml包来编/解码XML文件,重要的一点是对XML的所有操作都是通过struct tag来实现的,所以学会对struct tag的运用变得非常重要,在文章中我们简要的列举了如何定义tag。更多内容或tag定义请参看相应的官方资料。
0 commit comments