[golang] 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
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)
}
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"`
<name> <first>Asta</first> <last>Xie</last> </name>
转载自:https://juejin.cn/post/7045910244844634149