diff --git a/zh/07.1.md b/zh/07.1.md index 1731f5786..235dc9640 100644 --- a/zh/07.1.md +++ b/zh/07.1.md @@ -6,17 +6,17 @@ XML作为一种数据交换和信息传递的格式已经十分普及。而随 假如你是一名运维人员,你为你所管理的所有服务器生成了如下内容的xml的配置文件: ```xml - - - - Shanghai_VPN - 127.0.0.1 - - - Beijing_VPN - 127.0.0.2 - - + + + + Shanghai_VPN + 127.0.0.1 + + + Beijing_VPN + 127.0.0.2 + + ``` 上面的XML文档描述了两个服务器的信息,包含了服务器名和服务器的IP信息,接下来的Go例子以此XML描述的信息进行操作。 @@ -24,77 +24,77 @@ XML作为一种数据交换和信息传递的格式已经十分普及。而随 如何解析如上这个XML文件呢? 我们可以通过xml包的`Unmarshal`函数来达到我们的目的 ```Go - func Unmarshal(data []byte, v interface{}) error +func Unmarshal(data []byte, v interface{}) error ``` data接收的是XML数据流,v是需要输出的结构,定义为interface,也就是可以把XML转换为任意的格式。我们这里主要介绍struct的转换,因为struct和XML都有类似树结构的特征。 示例代码如下: ```Go - 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"` +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("servers.xml") // For read access. + if err != nil { + fmt.Printf("error: %v", err) + return } - - type server struct { - XMLName xml.Name `xml:"server"` - ServerName string `xml:"serverName"` - ServerIP string `xml:"serverIP"` + defer file.Close() + data, err := ioutil.ReadAll(file) + if err != nil { + fmt.Printf("error: %v", err) + return } - - func main() { - file, err := os.Open("servers.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) + v := Recurlyservers{} + err = xml.Unmarshal(data, &v) + if err != nil { + fmt.Printf("error: %v", err) + return } + fmt.Println(v) +} + ``` XML本质上是一种树形的数据格式,而我们可以定义与之匹配的go 语言的 struct类型,然后通过xml.Unmarshal来将xml中的数据解析成对应的struct对象。如上例子输出如下数据 ```xml - {{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}] - - Shanghai_VPN - 127.0.0.1 - - - Beijing_VPN - 127.0.0.2 - - } +{{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}] + + Shanghai_VPN + 127.0.0.1 + + + Beijing_VPN + 127.0.0.2 + +} ``` 上面的例子中,将xml文件解析成对应的struct对象是通过`xml.Unmarshal`来完成的,这个过程是如何实现的?可以看到我们的struct定义后面多了一些类似于`xml:"serverName"`这样的内容,这个是struct的一个特性,它们被称为 struct tag,它们是用来辅助反射的。我们来看一下`Unmarshal`的定义: ```Go - func Unmarshal(data []byte, v interface{}) error +func Unmarshal(data []byte, v interface{}) error ``` 我们看到函数定义了两个参数,第一个是XML数据流,第二个是存储的对应类型,目前支持struct、slice和string,XML包内部采用了反射来进行数据的映射,所以v里面的字段必须是导出的。`Unmarshal`解析的时候XML元素和字段怎么对应起来的呢?这是有一个优先级读取流程的,首先会读取struct tag,如果没有,那么就会对应字段名。必须注意一点的是解析的时候tag、字段名、XML元素都是大小写敏感的,所以必须一一对应字段。 @@ -106,14 +106,14 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的 ```xml - - Shanghai_VPN - 127.0.0.1 - - - Beijing_VPN - 127.0.0.2 - + + Shanghai_VPN + 127.0.0.1 + + + Beijing_VPN + 127.0.0.2 + ``` @@ -133,61 +133,61 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的 假若我们不是要解析如上所示的XML文件,而是生成它,那么在go语言中又该如何实现呢? xml包中提供了`Marshal`和`MarshalIndent`两个函数,来满足我们的需求。这两个函数主要的区别是第二个函数会增加前缀和缩进,函数的定义如下所示: ```Go - func Marshal(v interface{}) ([]byte, error) - func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) +func Marshal(v interface{}) ([]byte, error) +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) ``` 两个函数第一个参数是用来生成XML的结构定义类型数据,都是返回生成的XML数据流。 下面我们来看一下如何输出如上的XML: ```Go - package main - - import ( - "encoding/xml" - "fmt" - "os" - ) - - type Servers struct { - XMLName xml.Name `xml:"servers"` - Version string `xml:"version,attr"` - Svs []server `xml:"server"` +package main + +import ( + "encoding/xml" + "fmt" + "os" +) + +type Servers struct { + XMLName xml.Name `xml:"servers"` + Version string `xml:"version,attr"` + Svs []server `xml:"server"` +} + +type server struct { + ServerName string `xml:"serverName"` + ServerIP string `xml:"serverIP"` +} + +func main() { + v := &Servers{Version: "1"} + v.Svs = append(v.Svs, server{"Shanghai_VPN", "127.0.0.1"}) + v.Svs = append(v.Svs, server{"Beijing_VPN", "127.0.0.2"}) + output, err := xml.MarshalIndent(v, " ", " ") + if err != nil { + fmt.Printf("error: %v\n", err) } + os.Stdout.Write([]byte(xml.Header)) - type server struct { - ServerName string `xml:"serverName"` - ServerIP string `xml:"serverIP"` - } - - func main() { - v := &Servers{Version: "1"} - v.Svs = append(v.Svs, server{"Shanghai_VPN", "127.0.0.1"}) - v.Svs = append(v.Svs, server{"Beijing_VPN", "127.0.0.2"}) - output, err := xml.MarshalIndent(v, " ", " ") - if err != nil { - fmt.Printf("error: %v\n", err) - } - os.Stdout.Write([]byte(xml.Header)) - - os.Stdout.Write(output) - } + os.Stdout.Write(output) +} ``` 上面的代码输出如下信息: ```xml - - - - Shanghai_VPN - 127.0.0.1 - - - Beijing_VPN - 127.0.0.2 - - + + + + Shanghai_VPN + 127.0.0.1 + + + Beijing_VPN + 127.0.0.2 + + ``` 和我们之前定义的文件的格式一模一样,之所以会有`os.Stdout.Write([]byte(xml.Header))` 这句代码的出现,是因为`xml.MarshalIndent`或者`xml.Marshal`输出的信息都是不带XML头的,为了生成正确的xml文件,我们使用了xml包预定义的Header变量。 @@ -220,13 +220,13 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的 - tag中含有`"a>b>c"`,那么就会循环输出三个元素a包含b,b包含c,例如如下代码就会输出 ```xml - FirstName string `xml:"name>first"` - LastName string `xml:"name>last"` + FirstName string `xml:"name>first"` + LastName string `xml:"name>last"` - - Asta - Xie - + + Asta + Xie + ``` 上面我们介绍了如何使用Go语言的xml包来编/解码XML文件,重要的一点是对XML的所有操作都是通过struct tag来实现的,所以学会对struct tag的运用变得非常重要,在文章中我们简要的列举了如何定义tag。更多内容或tag定义请参看相应的官方资料。