最常见错误

struct 字段小写,序列化反序列化都无法访问——无法外部访问

解析 XML,反序列化,Unmarshal

  1. 如果 struct 的一个字段是 string 或者[]byt 类型且它的 tag 含有",inner xml “,Unmarshal 将会将此字段所对应的元素内所有内嵌的原始 xml 累加到此字段上 比如下边例子中的 description,最终输出为:

    <server>
     <serverName>Shanghai_VPN</serverName>
     <serverIP>127.0.0.1</serverIP>
     </server>
     <server>
         <serverName>Beijing_VPN</serverName>
         <serverIP>127.0.0.2</serverIP>
     </server>
    
  2. 如果 struct 中有一个叫做 XMLName(必须是这个),且类型为 xml .Name 字段,那么在解析的时候就会保存这个 element 的名字到该字段,如下例子中的 servers 。

    { servers}
    
  3. 如果某个 struct 字段的 tag 定义中含有 XML 结构中 element 的名称,那么解析的时候就会把相应的 element 值赋值给该字段,如下 servername 和 serverip 定义。

  4. 如果某个 struct 字段的 tag 定义了中含有”,attr",那么解析的时候就会将该结构所对应的 element 的与字段同名的属性的值赋值给该字段,如下 version 定义。

  5. 如果某个 struct 字段的 tag 定义 型如"a>b>c",则解析的时候,会将 xml 结构 a 下面的 b 下面的 c 元素的值赋值给该字段。

  6. 如果某个 struct 字段的 tag 定义了"-",那么不会为该字段解析匹配任何 xml 数据。

  7. 如果 struct 字段后面的 tag 定义了",any",如果他的子元素在不满足其他的规则的时候就会匹配到这个字段。

  8. 如果某个 XML 元素包含一条或者多条注释,那么这些注释将被累加到第一个 tag 含有",comments"的字段上,这个字段的类型可能是[]byte 或 string,如果没有这样的字段存在,那么注释将会被抛弃。

只要设置对了 tag,那么 XML 解析就如上面示例般简单,tag 和 XML 的 element 是一一对应的关系, 如下所示,我们还可以通过 slice 来表示多个同级元素。

例子:

package main

import (
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "os"
)

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"`
}

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("===result====")
    fmt.Println(v)
    fmt.Println("===result====")

    fmt.Println("===description====")
    fmt.Println(v.Description)
    fmt.Println("===description====")
}
// out
// ===result====
// {{ servers} 1 [{Shanghai_VPN 127.0.0.1} {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>
// }
// ===result====
// ===description====

//     <server>
//         <serverName>Shanghai_VPN</serverName>
//         <serverIP>127.0.0.1</serverIP>
//     </server>
//     <server>
//         <serverName>Beijing_VPN</serverName>
//         <serverIP>127.0.0.2</serverIP>
//     </server>

// ===description====

官方例子:

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)

输出:

XMLName: xml.Name{Space:"", Local:"Person"}
Name: "Grace R. Emlin"
Phone: "none"
Email: [{home gre@example.com} {work gre@work.com}]
Groups: [Friends Squash]
Address: {Hanga Roa Easter Island}

生成 xml,序列化,Marshal

func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) // 带缩进,前缀

我们看到 Marshal 函数接收的参数 v 是 interface{} 类型的,即它可以接受任意类型的参数,那么 xml 包,根据什么规则来生成相应的 XML 文件呢?

  1. 如果 v 是 array 或者 slice,那么输出每一个元素,类似 value
  2. 如果 v 是指针,那么会 Marshal 指针指向的内容,如果指针为空,什么都不输出
  3. 如果 v 是 interface,那么就处理 interface 所包含的数据
  4. 如果 v 是其他数据类型,就会输出这个数据类型所拥有的字段信息

生成的 XML 文件中的 element 的名字又是根据什么决定的呢?元素名按照如下优先级从 struct 中获取:

  1. 如果 v 是 struct,XMLName 的 tag 中定义的名称
  2. 类型为 xml.Name 的名叫 XMLName 的字段的值
  3. 通过 strcut 中字段的 tag 来获取
  4. 通过 strcut 的字段名用来获取
  5. marshal 的类型名称

我们应如何设置 struct 中字段的 tag 信息以控制最终 xml 文件的生成呢?

  1. XMLName 不会被输出
  2. tag 中含有"-“的字段不会输出
  3. tag 中含有"name,attr”,会以 name 作为属性名,字段值作为值输出为这个 XML 元素的属性,如上 version 字段所描述
  4. tag 中含有",attr",会以这个 struct 的字段名作为属性名输出为 XML 元素的属性,类似上一条,只是这个 name 默认是字段名了。
  5. tag 中含有",chardata",输出为 xml 的 character data 而非 element。
  6. tag 中含有",innerxml",将会被原样输出,而不会进行常规的编码过程
  7. tag 中含有",comment",将被当作 xml 注释来输出,而不会进行常规的编码过程,字段值中不能含有"–“字符串
  8. tag 中含有"omitempty”,如果该字段的值为空值那么该字段就不会被输出到 xml ,空值包括:false、0、nil 指针或 nil 接口,任何长度为 0 的 array, slice, map 或者 string
  9. tag 中含有"a>b>c",那么就会循环输出三个元素 a 包含 b,b 包含 c,例如如下代码就会输出
# Input
FirstName string `xml:"name>first"`
LastName string `xml:"name>last"`
#output
<name>
<first>Asta</first>
<last>Xie</last>
</name>

Json 概述

  • JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于让人阅读。
  • JSON 与 XML 最大的不同在于 XML 是一个完整的标记语言,而 JSON 不是。
  • JSON 由于比 XML 更小、更快,更易解析,以及浏览器的内建快速解析支持,使得其更适用于网络数据传输领域。

解析 json

解析到结构体

解析到 interface

Go 类型和 JSON 类型的对应关系如下:

  • bool 代表 JSON booleans,
  • float64 代表 JSON numbers,
  • string 代表 JSON strings,
  • nil 代表 JSON null.

解析到 map[string]interface

键必须是 string

interface 到指定类型

通过断言之后,你就可以通过如下方式来访问里面的数据了

for k, v := range m {
    switch vv := v.(type) {
    case string:
        fmt.Println(k, "is string", vv)
    case int:
        fmt.Println(k, "is int", vv)
    case []interface{}:
        fmt.Println(k, "is an array:")
        for i, u := range vv {
            fmt.Println(i, u)
            }
    default:
    fmt.Println(k, "is of a type I don't know how to handle")
        }
    }

生成 json,序列化,Marshal

针对 JSON 的输出,我们在定义 struct tag 的时候需要注意的几点是:

  1. 字段的 tag 是 “-",那么这个字段不会输出到 JSON
  2. tag 中带有自定义名称,那么这个自定义名称会出现在 JSON 的字段名中
  3. tag 中如果带有"omitempty"选项,那么如果该字段值为空,就不会输出到 JSON 串中
  4. 如果字段类型是 bool, string, int, int64 等,而 tag 中带有”,string"选项,那么这个字段在输出到 JSON 的时候会把该字段对应的值转换成 JSON 字符串

更好用的 github.com/bitly/go-simplejson

不用进行复杂的类型断言

package main

import (
    "fmt"
    "github.com/bitly/go-simplejson"
)

func main() {
    js, err := simplejson.NewJson([]byte(`
        {
        "test": {
        "array": [1, "2", 3],
        "int": 10,
        "float": 5.150,
        "bignum": 9223372036854775807,
        "string": "simplejson",
        "bool": true
        }
        }`))

    if err != nil {
        panic(err)
    }
    arr, _ := js.Get("test").Get("array").Array()
    i, _ := js.Get("test").Get("int").Int()
    ms := js.Get("test").Get("string").MustString("test")
    ms2 := js.Get("test").Get("string2").MustString("test")

    fmt.Println("arr:", arr)
    fmt.Println(i)
    fmt.Println(ms)
    fmt.Println(ms2)
}

参考

  1. 《go web 编程》
  2. 官方文档:https://studygolang.com/pkgdoc