在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 7.1 XML處理
7 文本處理
3 Web基礎(chǔ)
14 擴(kuò)展Web框架
10.4 小結(jié)
2.2 Go基礎(chǔ)
2.8 總結(jié)
6.1 session和cookie
5.5 使用beedb庫進(jìn)行ORM開發(fā)
8.3 REST
13.6 小結(jié)
5.4 使用PostgreSQL數(shù)據(jù)庫
14.6 pprof支持
14.1 靜態(tài)文件支持
11.2 使用GDB調(diào)試
7.7 小結(jié)
1 GO環(huán)境配置
14.5 多語言支持
7.1 XML處理
1.5 總結(jié)
13 如何設(shè)計(jì)一個(gè)Web框架
14.3 表單及驗(yàn)證支持
12 部署與維護(hù)
10 國際化和本地化
1.1 Go 安裝
6.2 Go如何使用session
5.6 NOSQL數(shù)據(jù)庫操作
6.5 小結(jié)
9.4 避免SQL注入
12.1 應(yīng)用日志
4.2 驗(yàn)證表單的輸入
10.1 設(shè)置默認(rèn)地區(qū)
1.3 Go 命令
9.6 加密和解密數(shù)據(jù)
4.1 處理表單的輸入
4.4 防止多次遞交表單
11.3 Go怎么寫測試用例
8 Web服務(wù)
12.3 應(yīng)用部署
5.7 小結(jié)
12.5 小結(jié)
11 錯(cuò)誤處理,調(diào)試和測試
9.2 確保輸入過濾
14.2 Session支持
6.4 預(yù)防session劫持
12.4 備份和恢復(fù)
8.1 Socket編程
13.1 項(xiàng)目規(guī)劃
13.4 日志和配置設(shè)計(jì)
7.6 字符串處理
13.2 自定義路由器設(shè)計(jì)
6.3 session存儲(chǔ)
3.4 Go的http包詳解
8.2 WebSocket
10.3 國際化站點(diǎn)
7.5 文件操作
7.4 模板處理
9.1 預(yù)防CSRF攻擊
13.3 controller設(shè)計(jì)
2.6 interface
14.4 用戶認(rèn)證
2.3 流程和函數(shù)
附錄A 參考資料
11.1 錯(cuò)誤處理
9.5 存儲(chǔ)密碼
9.3 避免XSS攻擊
12.2 網(wǎng)站錯(cuò)誤處理
6 session和數(shù)據(jù)存儲(chǔ)
2.4 struct類型
3.3 Go如何使得Web工作
2.5 面向?qū)ο?/span>
3.1 Web工作方式
1.2 GOPATH與工作空間
2.1 你好,Go
9.7 小結(jié)
13.5 實(shí)現(xiàn)博客的增刪改
7.2 JSON處理
10.2 本地化資源
7.3 正則處理
2 Go語言基礎(chǔ)
5.1 database/sql接口
4.5 處理文件上傳
8.5 小結(jié)
4.3 預(yù)防跨站腳本
5.3 使用SQLite數(shù)據(jù)庫
14.7 小結(jié)
3.2 Go搭建一個(gè)Web服務(wù)器
2.7 并發(fā)
5 訪問數(shù)據(jù)庫
4 表單
3.5 小結(jié)
1.4 Go開發(fā)工具
11.4 小結(jié)
9 安全與加密
5.2 使用MySQL數(shù)據(jù)庫
4.6 小結(jié)
8.4 RPC

7.1 XML處理

XML作為一種數(shù)據(jù)交換和信息傳遞的格式已經(jīng)十分普及。而隨著Web服務(wù)日益廣泛的應(yīng)用,現(xiàn)在XML在日常的開發(fā)工作中也扮演了愈發(fā)重要的角色。這一小節(jié), 我們將就Go語言標(biāo)準(zhǔn)包中的XML相關(guān)處理的包進(jìn)行介紹。

這個(gè)小節(jié)不會(huì)涉及XML規(guī)范相關(guān)的內(nèi)容(如需了解相關(guān)知識(shí)請參考其他文獻(xiàn)),而是介紹如何用Go語言來編解碼XML文件相關(guān)的知識(shí)。

假如你是一名運(yùn)維人員,你為你所管理的所有服務(wù)器生成了如下內(nèi)容的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文檔描述了兩個(gè)服務(wù)器的信息,包含了服務(wù)器名和服務(wù)器的IP信息,接下來的Go例子以此XML描述的信息進(jìn)行操作。

解析XML

如何解析如上這個(gè)XML文件呢? 我們可以通過xml包的Unmarshal函數(shù)來達(dá)到我們的目的

func Unmarshal(data []byte, v interface{}) error

data接收的是XML數(shù)據(jù)流,v是需要輸出的結(jié)構(gòu),定義為interface,也就是可以把XML轉(zhuǎn)換為任意的格式。我們這里主要介紹struct的轉(zhuǎn)換,因?yàn)閟truct和XML都有類似樹結(jié)構(gòu)的特征。

示例代碼如下:

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
    }
    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本質(zhì)上是一種樹形的數(shù)據(jù)格式,而我們可以定義與之匹配的go 語言的 struct類型,然后通過xml.Unmarshal來將xml中的數(shù)據(jù)解析成對應(yīng)的struct對象。如上例子輸出如下數(shù)據(jù)

{{ 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文件解析成對應(yīng)的struct對象是通過xml.Unmarshal來完成的,這個(gè)過程是如何實(shí)現(xiàn)的?可以看到我們的struct定義后面多了一些類似于xml:"serverName"這樣的內(nèi)容,這個(gè)是struct的一個(gè)特性,它們被稱為 struct tag,它們是用來輔助反射的。我們來看一下Unmarshal的定義:

func Unmarshal(data []byte, v interface{}) error

我們看到函數(shù)定義了兩個(gè)參數(shù),第一個(gè)是XML數(shù)據(jù)流,第二個(gè)是存儲(chǔ)的對應(yīng)類型,目前支持struct、slice和string,XML包內(nèi)部采用了反射來進(jìn)行數(shù)據(jù)的映射,所以v里面的字段必須是導(dǎo)出的。Unmarshal解析的時(shí)候XML元素和字段怎么對應(yīng)起來的呢?這是有一個(gè)優(yōu)先級讀取流程的,首先會(huì)讀取struct tag,如果沒有,那么就會(huì)對應(yīng)字段名。必須注意一點(diǎn)的是解析的時(shí)候tag、字段名、XML元素都是大小寫敏感的,所以必須一一對應(yīng)字段。

Go語言的反射機(jī)制,可以利用這些tag信息來將來自XML文件中的數(shù)據(jù)反射成對應(yīng)的struct對象,關(guān)于反射如何利用struct tag的更多內(nèi)容請參閱reflect中的相關(guān)內(nèi)容。

解析XML到struct的時(shí)候遵循如下的規(guī)則:

  • 如果struct的一個(gè)字段是string或者[]byte類型且它的tag含有",innerxml",Unmarshal將會(huì)將此字段所對應(yīng)的元素內(nèi)所有內(nèi)嵌的原始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中有一個(gè)叫做XMLName,且類型為xml.Name字段,那么在解析的時(shí)候就會(huì)保存這個(gè)element的名字到該字段,如上面例子中的servers。
  • 如果某個(gè)struct字段的tag定義中含有XML結(jié)構(gòu)中element的名稱,那么解析的時(shí)候就會(huì)把相應(yīng)的element值賦值給該字段,如上servername和serverip定義。
  • 如果某個(gè)struct字段的tag定義了中含有",attr",那么解析的時(shí)候就會(huì)將該結(jié)構(gòu)所對應(yīng)的element的與字段同名的屬性的值賦值給該字段,如上version定義。
  • 如果某個(gè)struct字段的tag定義 型如"a>b>c",則解析的時(shí)候,會(huì)將xml結(jié)構(gòu)a下面的b下面的c元素的值賦值給該字段。
  • 如果某個(gè)struct字段的tag定義了"-",那么不會(huì)為該字段解析匹配任何xml數(shù)據(jù)。
  • 如果struct字段后面的tag定義了",any",如果他的子元素在不滿足其他的規(guī)則的時(shí)候就會(huì)匹配到這個(gè)字段。
  • 如果某個(gè)XML元素包含一條或者多條注釋,那么這些注釋將被累加到第一個(gè)tag含有",comments"的字段上,這個(gè)字段的類型可能是[]byte或string,如果沒有這樣的字段存在,那么注釋將會(huì)被拋棄。

上面詳細(xì)講述了如何定義struct的tag。 只要設(shè)置對了tag,那么XML解析就如上面示例般簡單,tag和XML的element是一一對應(yīng)的關(guān)系,如上所示,我們還可以通過slice來表示多個(gè)同級元素。

注意: 為了正確解析,go語言的xml包要求struct定義中的所有字段必須是可導(dǎo)出的(即首字母大寫)

輸出XML

假若我們不是要解析如上所示的XML文件,而是生成它,那么在go語言中又該如何實(shí)現(xiàn)呢? xml包中提供了MarshalMarshalIndent兩個(gè)函數(shù),來滿足我們的需求。這兩個(gè)函數(shù)主要的區(qū)別是第二個(gè)函數(shù)會(huì)增加前綴和縮進(jìn),函數(shù)的定義如下所示:

func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

兩個(gè)函數(shù)第一個(gè)參數(shù)是用來生成XML的結(jié)構(gòu)定義類型數(shù)據(jù),都是返回生成的XML數(shù)據(jù)流。

下面我們來看一下如何輸出如上的XML:

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))

    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>

和我們之前定義的文件的格式一模一樣,之所以會(huì)有os.Stdout.Write([]byte(xml.Header)) 這句代碼的出現(xiàn),是因?yàn)?code>xml.MarshalIndent或者xml.Marshal輸出的信息都是不帶XML頭的,為了生成正確的xml文件,我們使用了xml包預(yù)定義的Header變量。

我們看到Marshal函數(shù)接收的參數(shù)v是interface{}類型的,即它可以接受任意類型的參數(shù),那么xml包,根據(jù)什么規(guī)則來生成相應(yīng)的XML文件呢?

  • 如果v是 array或者slice,那么輸出每一個(gè)元素,類似value
  • 如果v是指針,那么會(huì)Marshal指針指向的內(nèi)容,如果指針為空,什么都不輸出
  • 如果v是interface,那么就處理interface所包含的數(shù)據(jù)
  • 如果v是其他數(shù)據(jù)類型,就會(huì)輸出這個(gè)數(shù)據(jù)類型所擁有的字段信息

生成的XML文件中的element的名字又是根據(jù)什么決定的呢?元素名按照如下優(yōu)先級從struct中獲?。?/p>

  • 如果v是struct,XMLName的tag中定義的名稱
  • 類型為xml.Name的名叫XMLName的字段的值
  • 通過struct中字段的tag來獲取
  • 通過struct的字段名用來獲取
  • marshall的類型名稱

我們應(yīng)如何設(shè)置struct 中字段的tag信息以控制最終xml文件的生成呢?

  • XMLName不會(huì)被輸出
  • tag中含有"-"的字段不會(huì)輸出
  • tag中含有"name,attr",會(huì)以name作為屬性名,字段值作為值輸出為這個(gè)XML元素的屬性,如上version字段所描述
  • tag中含有",attr",會(huì)以這個(gè)struct的字段名作為屬性名輸出為XML元素的屬性,類似上一條,只是這個(gè)name默認(rèn)是字段名了。
  • tag中含有",chardata",輸出為xml的 character data而非element。
  • tag中含有",innerxml",將會(huì)被原樣輸出,而不會(huì)進(jìn)行常規(guī)的編碼過程
  • tag中含有",comment",將被當(dāng)作xml注釋來輸出,而不會(huì)進(jìn)行常規(guī)的編碼過程,字段值中不能含有"--"字符串
  • tag中含有"omitempty",如果該字段的值為空值那么該字段就不會(huì)被輸出到XML,空值包括:false、0、nil指針或nil接口,任何長度為0的array, slice, map或者string
  • tag中含有"a>b>c",那么就會(huì)循環(huán)輸出三個(gè)元素a包含b,b包含c,例如如下代碼就會(huì)輸出

      FirstName string   `xml:"name>first"`
      LastName  string   `xml:"name>last"`
    
      <name>
      <first>Asta</first>
      <last>Xie</last>
      </name>

上面我們介紹了如何使用Go語言的xml包來編/解碼XML文件,重要的一點(diǎn)是對XML的所有操作都是通過struct tag來實(shí)現(xiàn)的,所以學(xué)會(huì)對struct tag的運(yùn)用變得非常重要,在文章中我們簡要的列舉了如何定義tag。更多內(nèi)容或tag定義請參看相應(yīng)的官方資料。