go標准庫的學習-encoding/xml


參考:https://studygolang.com/pkgdoc

導入方式:

import "encoding/xml"

實現的簡單的理解XML命名空間的XML 1.0編譯器

 

func Unmarshal —— 用於解析XML文件

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

Unmarshal解析XML編碼的數據並將結果存入v指向的值。v只能指向結構體、切片或者和字符串。良好格式化的數據如果不能存入v,會被丟棄。

因為Unmarshal使用reflect包,它只能填寫導出字段。本函數好似用大小寫敏感的比較來匹配XML元素名和結構體的字段名/標簽鍵名。

Unmarshal函數使用如下規則將XML元素映射到結構體字段上。這些規則中,字段標簽指的是結構體字段的標簽鍵'xml'對應的值(參見上面的例子):

* 如果結構體字段的類型為字符串或者[]byte,且標簽為",innerxml",
  Unmarshal函數直接將對應原始XML文本寫入該字段,其余規則仍適用。
* 如果結構體字段類型為xml.Name且名為XMLName,Unmarshal會將元素名寫入該字段
* 如果字段XMLName的標簽的格式為"name""namespace-URL name",
  XML元素必須有給定的名字(以及可選的名字空間),否則Unmarshal會返回錯誤。
* 如果XML元素的屬性的名字匹配某個標簽",attr"為字段的字段名,或者匹配某個標簽為"name,attr"
  的字段的標簽名,Unmarshal會將該屬性的值寫入該字段。
* 如果XML元素包含字符數據,該數據會存入結構體中第一個具有標簽",chardata"的字段中,
  該字段可以是字符串類型或者[]byte類型。如果沒有這樣的字段,字符數據會丟棄。
* 如果XML元素包含注釋,該數據會存入結構體中第一個具有標簽",comment"的字段中,
  該字段可以是字符串類型或者[]byte類型。如果沒有這樣的字段,字符數據會丟棄。
* 如果XML元素包含一個子元素,其名稱匹配格式為"a""a>b>c"的標簽的前綴,反序列化會深入
  XML結構中尋找具有指定名稱的元素,並將最后端的元素映射到該標簽所在的結構體字段。
  以">"開始的標簽等價於以字段名開始並緊跟着">" 的標簽。
* 如果XML元素包含一個子元素,其名稱匹配某個結構體類型字段的XMLName字段的標簽名,
  且該結構體字段本身沒有顯式指定標簽名,Unmarshal會將該元素映射到該字段。
* 如果XML元素的包含一個子元素,其名稱匹配夠格結構體字段的字段名,且該字段沒有任何模式選項
  (",attr"",chardata"等),Unmarshal會將該元素映射到該字段。
* 如果XML元素包含的某個子元素不匹配以上任一條,而存在某個字段其標簽為",any",
  Unmarshal會將該元素映射到該字段。
* 匿名字段被處理為其字段好像位於外層結構體中一樣。
* 標簽為"-"的結構體字段永不會被反序列化填寫。

Unmarshal函數將XML元素寫入string或[]byte時,會將該元素的字符數據串聯起來作為值,目標[]byte不能是nil。

Unmarshal函數將屬性寫入string或[]byte時,會將屬性的值以字符串/切片形式寫入。

Unmarshal函數將XML元素寫入切片時,會將切片擴展並將XML元素的子元素映射入新建的值里。

Unmarshal函數將XML元素/屬性寫入bool值時,會將對應的字符串轉化為布爾值。

Unmarshal函數將XML元素/屬性寫入整數或浮點數類型時,會將對應的字符串解釋為十進制數字。不會檢查溢出。

Unmarshal函數將XML元素寫入xml.Name類型時,會記錄元素的名稱。

Unmarshal函數將XML元素寫入指針時,會申請一個新值並將XML元素映射入該值。

舉例:

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>

舉例:

package main 
import(
    "fmt"
    "encoding/xml"
    "io/ioutil"
    "os"
    "log"
)
type Recurlyservers struct {//后面的內容是struct tag,標簽,是用來輔助反射的
    XMLName xml.Name `xml:"servers"` //將元素名寫入該字段
    Version string `xml:"version,attr"` //將version該屬性的值寫入該字段
    Svs []server `xml:"server"`
    Description string `xml:",innerxml"` //Unmarshal函數直接將對應原始XML文本寫入該字段
}

type server struct{
    XMLName xml.Name `xml:"server"`
    ServerName string `xml:"serverName"`
    ServerIP string `xml:"serverIP"`
}
func main() {
    file, err := os.Open("servers.xml")
    if err != nil {
        log.Fatal(err)
    }

    defer file.Close()
    data, err := ioutil.ReadAll(file)
    if err != nil {
        log.Fatal(err)
    }

    v := Recurlyservers{}
    err = xml.Unmarshal(data, &v)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(v) 

    fmt.Printf("XMLName: %#v\n", v.XMLName)
    fmt.Printf("Version: %q\n", v.Version)

    fmt.Printf("Server: %v\n", v.Svs)
    for i, svs := range v.Svs{
        fmt.Println(i)
        fmt.Printf("Server XMLName: %#v\n", svs.XMLName)
        fmt.Printf("Server ServerName: %q\n", svs.ServerName)
        fmt.Printf("Server ServerIP: %q\n", svs.ServerIP)        
    }
    fmt.Printf("Description: %q\n", v.Description)

}

返回:

userdeMBP:go-learning user$ go run test.go
{{ 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>
}
XMLName: xml.Name{Space:"", Local:"servers"}
Version: "1"
Server: [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}]
0
Server XMLName: xml.Name{Space:"", Local:"server"}
Server ServerName: "Shanghai_VPN"
Server ServerIP: "127.0.0.1"
1
Server XMLName: xml.Name{Space:"", Local:"server"}
Server ServerName: "Beijing_VPN"
Server ServerIP: "127.0.0.2"
Description: "\n    <server>\n        <serverName>Shanghai_VPN</serverName>\n        <serverIP>127.0.0.1</serverIP>\n    </server>\n    <server>\n        <serverName>Beijing_VPN</serverName>\n        <serverIP>127.0.0.2</serverIP>\n    </server>\n"

 

生成XML文件使用下面的兩個函數:

func Marshal

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

Marshal函數返回v的XML編碼。

Marshal處理數組或者切片時會序列化每一個元素。Marshal處理指針時,會序列化其指向的值;如果指針為nil,則啥也不輸出。Marshal處理接口時,會序列化其內包含的具體類型值,如果接口值為nil,也是不輸出。Marshal處理其余類型數據時,會輸出一或多個包含數據的XML元素。

XML元素的名字按如下優先順序獲取:

- 如果數據是結構體,其XMLName字段的標簽
- 類型為xml.Name的XMLName字段的值
- 數據是某結構體的字段,其標簽
- 數據是某結構體的字段,其字段名
- 被序列化的類型的名字

一個結構體的XML元素包含該結構體所有導出字段序列化后的元素,有如下例外:

- XMLName字段,如上所述,會省略
- 具有標簽"-"的字段會省略
- 具有標簽"name,attr"的字段會成為該XML元素的名為name的屬性
- 具有標簽",attr"的字段會成為該XML元素的名為字段名的屬性
- 具有標簽",chardata"的字段會作為字符數據寫入,而非XML元素
- 具有標簽",innerxml"的字段會原樣寫入,而不會經過正常的序列化過程
- 具有標簽",comment"的字段作為XML注釋寫入,而不經過正常的序列化過程,該字段內不能有"--"字符串
- 標簽中包含"omitempty"選項的字段如果為空值會省略
  空值為false、0、nil指針、nil接口、長度為0的數組、切片、映射
- 匿名字段(其標簽無效)會被處理為其字段是外層結構體的字段

如果一個字段的標簽為"a>b>c",則元素c將會嵌套進其上層元素a和b中。如果該字段相鄰的字段標簽指定了同樣的上層元素,則會放在同一個XML元素里。

參見MarshalIndent的例子。如果要求Marshal序列化通道、函數或者映射會返回錯誤。

func MarshalIndent

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

MarshalIndent功能類似Marshal。但每個XML元素會另起一行並縮進,該行以prefix起始,后跟一或多個indent的拷貝(根據嵌套層數)。它只是多了縮進的設定,使得生成的XML文件可讀性更高

兩個函數第一個參數是用來生成XML的結構定義類型數據,都是返回生成的XML數據流

舉例生成上面解析的XML文件:

package main 
import(
    "fmt" "encoding/xml" "os" ) type Servers struct {//后面的內容是struct tag,標簽,是用來輔助反射的 XMLName xml.Name `xml:"servers"` //將元素名寫入該字段 Version string `xml:"version,attr"` //將version該屬性的值寫入該字段 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"}) //每個XML元素會另起一行並縮進,每行以prefix(這里為兩個空格)起始,后跟一或多個indent(這里為四個空格)的拷貝(根據嵌套層數) //即第一層嵌套只遞進四個空格,第二層嵌套則遞進八個空格 output, err := xml.MarshalIndent(v," ", " ") if err != nil{ fmt.Printf("error : %v\n", err) } os.Stdout.Write([]byte(xml.Header)) //輸出預定義的xml頭 <?xml version="1.0" encoding="UTF-8"?>  os.Stdout.Write(output) }

返回:

userdeMBP:go-learning user$ go run test.go
<?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))這句代碼是因為上面的兩個函數輸出的信息都是不帶XML頭的,為了生成正確的xml文件,需要使用xml包預定義的Header變量

 

Constants 

 

const (
    // 適用於本包Marshal輸出的一般性XML header
    // 本常數並不會自動添加到本包的輸出里,這里提供主要是出於便利的目的
    Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
)

 

 

另一個例子:

package main 
import(
    "fmt" "encoding/xml" "os" ) type Address struct { City, State string } type Person struct { XMLName xml.Name `xml:"person"` //該XML文件的根元素為person Id int `xml:"id,attr"` //該值會作為person元素的屬性 FirstName string `xml:"name>first"` //first為name的子元素 LastName string `xml:"name>last"` //last Age int `xml:"age"` Height float32 `xml:"height,omitempty"` //含omitempty選項的字段如果為空值會省略 Married bool //默認為false Address //匿名字段(其標簽無效)會被處理為其字段是外層結構體的字段,所以沒有Address這個元素,而是直接顯示City, State這兩個元素 Comment string `xml:",comment"` //注釋 } func main() { v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42} v.Comment = " Need more details. " v.Address = Address{"Hanga Roa", "Easter Island"} output, err := xml.MarshalIndent(v, " ", " ") if err != nil { fmt.Printf("error: %v\n", err) } os.Stdout.Write(output) }

返回:

userdeMBP:go-learning user$ go run test.go
  <person id="13"> <name> <first>John</first> <last>Doe</last> </name> <age>42</age> <Married>false</Married> <City>Hanga Roa</City> <State>Easter Island</State> <!-- Need more details. --> </person>

如果是用的是xml.Marshal(v),返回為:

userdeMBP:go-learning user$ go run test.go
<person id="13"><name><first>John</first><last>Doe</last></name><age>42</age><Married>false</Married><City>Hanga Roa</City><State>Easter Island</State><!-- Need more details. --></person>

可讀性就會變得差很多

 未完待續


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM