file, err := os.Open(
"servers.xml"
)
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)
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"`
{{ 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 文件解析成对应的 struct
是通过 xml.Unmarshal
来完成的,这个过程是如何实现的?
第一个参数是 xml 数据流,第二个参数是存储的对应类型,目前支持 struct
、slice
和 string
。xml 包内部采用了反射来进行数据的映射,所以 v
里面的字段必须是可导出的(即首字母大写)。
func Unmarshal(data []byte, v interface{}) error
解析 xml 到 struct
的时候遵循如下规则:
如果 struct
的一个字段是 string
或者 []byte
类型,并且它的 tag
含有 xml:",innerxml"
,那么 xml.Unmarshal
将会将此字段对应层级元素的子级元素所有原始 xml 累加到此字段上,如上面例子中的 Description
;
如果 struct
中有一个叫做 XMLName
,且类型为 xml.Name
字段,那么在解析的时候就会保存这个 element 的名字到该字段,如上面例子中的 servers
;
如果 struct
的字段 tag 中含有 ",attr"
,那么解析的时候就会将该结构同名属性的值赋给该字段,如上 version
定义;
如果 struct
的字段 tag 中含有 "a>b>c"
,将会将此字段对应层级元素的子级元素 a
下面的 b
下面的 c
元素的值赋值给该字段;
如果 struct
的字段 tag 中含有 "-"
,那么不会为该字段解析匹配任何 xml 数据;
如果 struct
的字段 tag 中含有 ",any"
,将会将此字段对应层级不满足其他规则的 xml 数据匹配到这个字段;
如果 struct
的字段 tag 中含有 ",comment"
,将会将此字段对应层级的注释累加到此字段上,这个字段的类型可以是 []byte
或 string
;
生成 XML
func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
因为 xml.MarshalIndent
或者 xml.Marshal
生成的信息是不带 xml 头的,因此需要执行 os.Stdout.Write([]byte(xml.Header))
。
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)
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"`
<?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>
Marshal
接收的参数 v
是 interface{}
类型的,即它可以接受任意类型的参数。
如果 v
是 array
或者 slice
,那么输出每一个元素,类似 value。
如果 v
是指针,那么会输出指针指向的内容,如果指针为空,则什么都不输出。
如果 v
是 interface
,那么就处理 interface
所包含的数据。
如果 v
是其他数据类型,就会输出这个数据类型所拥有的字段信息。
如何设置 struct
中字段的 tag 信息以控制最终 xml 文件的生成呢?(未指明部分可参考解析 xml 部分)
tag 中含有 ",chardata"
,生成为 xml 的 character data,而非 element。
tag 中含有 ",innerxml"
,将会被原样输出,而不会进行常规的编码过程。
tag 中含有 ",comment"
,将会被当作 xml 注释来输出,而不会进行常规的编码过程,字段值中不能含有 --
字符串。
tag 中含有 "omitempty"
,如果该字段的值为空值那么该字段就不会被输出到 xml,空值包括:false、0、nil 指针或 nil 接口,任何长度为 0 的 array、slice、map 或 string。
tag 中含有 "a>b>c"
,那么就会循环输出三个元素 a 包含 b,b 包含 c,例如如下代码就会输出:
FirstName string `xml:"name>first"`
LastName string `xml:"name>last"`
<first>Asta</first>
<last>Xie</last>
</name>