Go语言 encoding/xml
xml 和 json 都是文本表示的数据格式 跨系统 数据交换
XML 适合标记文档 JSON 适合数据交互
XML是 完整的 标记语言 JSON不是
XML 标记语言的特性 提供 绝佳的延展性 如XPath 在数据存储 扩展及高级检索方面优势明显
JSON 由于比XML 小巧 及浏览器的内建快速解析支持  更适用于网络数据传输
XML文档 可读性更高
JSON数据表示和传输性能 比XML简洁 格式简单 占用带宽少
{"employees":[
    { "firstName":"John", "lastName":"Doe" },
    { "firstName":"Anna", "lastName":"Smith" },
    { "firstName":"Peter", "lastName":"Jones" }
]}
<employees>
  <employee>
      <firstName>John</firstName> <lastName>Doe</lastName>
  </employee>
  <employee>
      <firstName>Anna</firstName> <lastName>Smith</lastName>
  </employee>
  <employee>
      <firstName>Peter</firstName> <lastName>Jones</lastName>
  </employee>
</employees>
JSON
Simple syntax, which results in less “markup” overhead compared to XML.
Easy to use with JavaScript as the markup is a subset of JS object literal notation and has the same basic data types as JavaScript.
JSON Schema for description and datatype and structure validation
JsonPath for extracting information in deeply nested structures

XML
Generalized markup; it is possible to create “dialects” for any kind of purpose
XML Schema for datatype, structure validation. Makes it also possible to create new datatypes
XSLT for transformation into different output formats
XPath/XQuery for extracting information in deeply nested structures
built in support for namespaces

go中 提供了encoding/xml package
encoding/xml
Package xml implements a simple XML 1.0 parser that understands XML name spaces.
xml 包实现了一个简单的 XML 1.0 语法分析器  能够理解 XML 命名空间
常量
const (
  // A generic XML header suitable for use with the output of Marshal.
  // This is not automatically added to any output of this package, it is provided as a convenience.
  Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
)


Marshal 函数
func Marshal(v interface{}) ([]byte, error)
Marshal returns the XML encoding of v

Marshal 在遇到 数组 或 切片 会对其包含的每个元素进行封装
在遇到指针时 对指针的值进行封装 忽略那些未 nil 的指针
在遇到接口时 对接口包含的值进行封装 忽略那些值为 nil 的接口
在遇到其他数据时  Marshal 将写入一个或多个包含这些数据的 XML 元素
在进行封装时  XML 元素的名字 由一系列 规则决定 这些规则的优先级从高到低依次为

如果给定的数据是一个结构  那么使用 XMLName 字段的标签作为元素名
使用类型为 Name 的 XMLName 字段的值为元素名
将用于获取数据的结构字段的标签用作元素名
将用于获取数据的结构字段的名字用作元素名
将被封装类型的名字用作元素名

结构中的每个已导出字段都会被封装为相应的元素并包含在 XML 里面 以下规则中提到的内容除外
XMLName 字段 因为前面提到的原因 会被忽略
带有 “-” 标签的字段会被忽略
带有 “name,attr” 标签的字段会成为 XML 元素的属性  其中属性的名字为这里给定的 name
带有 ”,attr” 标签的字段会成为 XML 元素的属性  其中属性的名字为字段的名字
带有 ”,chardata” 标签的字段将会被封装为字符数据而不是 XML 元素
带有 ”,cdata” 标签的字段将会被封装为字符数据而不是 XML 元素  并且这些数据还会被一个或多个
使用 xml.Name 的第一个名称作为整个 XML 文档的 根节点
凡是需要解析的XML内容 需要使用结构体的 tag 属性 反射出 xml 特性 包含 xml 名称 是否是属性 注释等
凡是需要解析的节点 结构体的成员名称 首字母必须要大些
可以直接跳跃解析的父节点 该父节点只存在逻辑结构,并不存储任何数据或者属性 可以使用 > 来表征
 

func Unmarshal(data []byte, v interface{}) error
Unmarshal parses the XML-encoded data and stores the result in the value pointed to by v, which must be an arbitrary struct, slice, or string. Well-formed data that does not fit into v is discarded.
package main
import (
    "encoding/xml"
    "fmt"
    "os"
)
func main() {
    type Address struct {
        City, State string
    }
    type Person struct {
        XMLName   xml.Name `xml:"person"`
        Id        int      `xml:"id,attr"`
        FirstName string   `xml:"name>first"`
        LastName  string   `xml:"name>last"`
        Age       int      `xml:"age"`
        Height    float32  `xml:"height,omitempty"`
        Married   bool
        Address
        Comment string `xml:",comment"`
    }

    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)
    fmt.Println("\n")
}


Unmarshal 函数
func Unmarshal(data []byte, v interface{}) error
Unmarshal 会对 XML 编码的数据进行语法分析  并将结果储存到 v 指向的值里面  其中 v 必须是一个任意的 arbitrary 结构 切片或者字符串
格式良好 但是无法放入到 v 里面的数据将被抛弃
package main
import (
    "encoding/xml"
    "fmt"
)
func main() {
  type Email struct {
      Where string `xml:"where,attr"`
      Addr  string
  }
  type Address struct {
      City, State string
  }
  type Result struct {
      XMLName xml.Name `xml:"Person"`
      Name    string   `xml:"FullName"`
      Phone   string
      Email   []Email
      Groups  []string `xml:"Group>Value"`
      Address
  }
  v := Result{Name: "none", Phone: "none"}
  data := `
      <Person>
          <FullName>Grace R. Emlin</FullName>
          <Company>Example Inc.</Company>
          <Email where="home">
              <Addr>gre@example.com</Addr>
          </Email>
          <Email where='work'>
              <Addr>gre@work.com</Addr>
          </Email>
          <Group>
              <Value>Friends</Value>
              <Value>Squash</Value>
          </Group>
          <City>Hanga Roa</City>
          <State>Easter Island</State>
      </Person>
  `
  err := xml.Unmarshal([]byte(data), &v)
  if err != nil {
      fmt.Printf("error: %v", err)
      return
  }
  fmt.Printf("XMLName: %#v\n", v.XMLName)
  fmt.Printf("Name: %q\n", v.Name)
  fmt.Printf("Phone: %q\n", v.Phone)
  fmt.Printf("Email: %v\n", v.Email)
  fmt.Printf("Groups: %v\n", v.Groups)
  fmt.Printf("Address: %v\n", v.Address)
}

Decoder
A Decoder represents an XML parser reading a particular input stream. The parser assumes that its input is encoded in UTF-8.
NewDecoder 函数
func NewDecoder(r io.Reader) *Decoder
创建一个新的读取 r 的 XML 语法分析器  如果 r 没有实现 io.ByteReader   那么函数将使用它自有的缓冲机制
(*Decoder) Decode 方法
func (d *Decoder) Decode(v interface{}) error
执行与 Unmarshal 一样的解码工作  唯一的不同在于这个方法会通过读取解码器流来查找起始元素
Encoder
Encoder 负责把 XML 数据写入至输出流里面
NewEncoder 函数
func NewEncoder(w io.Writer) *Encoder
返回一个能够对 w 进行写入的编码器
(*Encoder) Encode 方法
func (enc *Encoder) Encode(v interface{}) error
将 XML 编码的 v 写入到流里面
go读取写入xml文件
 
读取xml
读取 xml文件
<?xml version="1.0" encoding="UTF-8"?>
<azkaban-users>
  <user groups="azkaban" password="azkabanqweqe" roles="admin" username="azkaban">The demo</user>
</azkaban-users>
对应xml中内容编写结构体//xml文件中节点
type StrResources struct {
    XMLName xml.Name    `xml:"azkaban-users"`//标签名称
    Users []User `xml:"user"` //标签名称
}
type User struct {
    XMLName    xml.Name `xml:"user"` //标签名称
    UserName string   `xml:"username,attr"` //属性
    Roles string   `xml:"roles,attr"`
    Password string   `xml:"password,attr"`
    Groups string   `xml:"groups,attr"`
    InnerText  string   `xml:",innerxml"` //内部的文本
}

标签使用介绍
“-“ 不会输出
“name,attr” 以name作为属性名
“,attr” 以这个struct的字段名作为属性名输出为XML元素的属性
“,chardata” 输出为xml的 character data而非element
“,innerxml” 被原样输出
“,comment” 将被当作xml注释来输出,字段值中不能含有”–”字符串
“omitempty” 如果该字段的值为空值那么该字段就不会被输出到XML,空值包括 false、0、nil指针或nil接口,任何长度为0的array, slice, map或者string

main方法中进行读取操作
读取xml文件位置
将文件转为对象
遍历读取
func main(){ //获取xml文件位置
  content, err := ioutil.ReadFile("d:\\Users\\test2.xml")
  if err != nil {
      fmt.Println("读文件位置错误信息 ",err)
  }
  //将文件转换为对象
  var result StrResources
  err = xml.Unmarshal(content, &result)
  if err != nil {
      fmt.Println("读文件内容错误信息 ",err)
  }
  fmt.Println("result:",result)
  fmt.Println("result.ResourceString:",result.Users)
  for _,o :=range  result.Users{
      log.Println("UserName:",o.XMLName)
      log.Println("UserName:",o.UserName)
      log.Println("Roles:",o.Roles)
      log.Println("Password:",o.Password)
      log.Println("Groups:",o.Groups)
      log.Println("InnerText:",o.InnerText)
  }

  fmt.Println("读取完成。。。。")
}


写入xml
过程与写入是相逆的
把字符串转对象
遍历拼接
输出xml格式
保存文件至指定位置
func main()  {
  var text =`<azkaban-users></azkaban-users>`
  pars := &StrRes{}
  xml.Unmarshal([]byte(text), &pars)//将字符串转为对象
  fmt.Println(pars)
  v := &StrRes{Name: "helloWorld"}
  for cp:=0;cp<3;cp++  {
      var user UserA
      user.UserName="UserName"
      user.Password="Password"
      user.Groups=strconv.Itoa(cp)
      user.InnerText="123"
      v.Users=append(v.Users,user)
  }//输出打印格式
  xmlOutPut, outPutErr := xml.MarshalIndent(v, "  ", "    ")
  if outPutErr!=nil {
      fmt.Println("error:",outPutErr)
  }
  fmt.Println(string(xmlOutPut)) //保存文件
  if outPutErr == nil {
      headerBytes := []byte(xml.Header)//加入XML头
      xmlOutPutData := append(headerBytes, xmlOutPut...)//拼接XML头和实际XML内容  //写入文件
      ioutil.WriteFile("d:\\Users\\test4.xml", xmlOutPutData, os.ModeAppend)
      fmt.Println("输出完成。。。。")
  } else {
      fmt.Println(outPutErr)
  }
}