導入方式:
import "encoding/xml"
實作的簡單的了解XML命名空間的XML 1.0編譯器
funcUnmarshal —— 用于解析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檔案使用下面的兩個函數:
funcMarshal
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序列化通道、函數或者映射會傳回錯誤。
funcMarshalIndent
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>
可讀性就會變得差很多