99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Go XML處理

2022-05-13 17:44 更新

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)行介紹。

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

假如你是一名運(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文檔描述了兩個服務(wù)器的信息,包含了服務(wù)器名和服務(wù)器的IP信息,接下來的Go例子以此XML描述的信息進(jìn)行操作。

解析XML

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

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

我們看到函數(shù)定義了兩個參數(shù),第一個是XML數(shù)據(jù)流,第二個是存儲的對應(yīng)類型,目前支持struct、slice和string,XML包內(nèi)部采用了反射來進(jìn)行數(shù)據(jù)的映射,所以v里面的字段必須是導(dǎo)出的。Unmarshal解析的時(shí)候XML元素和字段怎么對應(yīng)起來的呢?這是有一個優(yōu)先級讀取流程的,首先會讀取struct tag,如果沒有,那么就會對應(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的一個字段是string或者[]byte類型且它的tag含有",innerxml",Unmarshal將會將此字段所對應(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中有一個叫做XMLName,且類型為xml.Name字段,那么在解析的時(shí)候就會保存這個element的名字到該字段,如上面例子中的servers。

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

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

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

輸出XML

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

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

兩個函數(shù)第一個參數(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>

和我們之前定義的文件的格式一模一樣,之所以會有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,那么輸出每一個元素,類似value
  • 如果v是指針,那么會Marshal指針指向的內(nèi)容,如果指針為空,什么都不輸出
  • 如果v是interface,那么就處理interface所包含的數(shù)據(jù)
  • 如果v是其他數(shù)據(jù)類型,就會輸出這個數(shù)據(jù)類型所擁有的字段信息

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

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

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

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

    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é)會對struct tag的運(yùn)用變得非常重要,在文章中我們簡要的列舉了如何定義tag。更多內(nèi)容或tag定義請參看相應(yīng)的官方資料。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號