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 数据流,第二个参数是存储的对应类型,目前支持 structslicestring。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",将会将此字段对应层级的注释累加到此字段上,这个字段的类型可以是 []bytestring
  • 生成 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 接收的参数 vinterface{} 类型的,即它可以接受任意类型的参数。

  • 如果 varray 或者 slice,那么输出每一个元素,类似 value。
  • 如果 v 是指针,那么会输出指针指向的内容,如果指针为空,则什么都不输出。
  • 如果 vinterface,那么就处理 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>
      
    分类:
    后端
    标签:
  •