參考: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>
可讀性就會變得差很多
未完待續