JSON作为一种在不同平台间的数据交换格式,Go的标准包encoding/json中实现了对json的编码(marshal)和解码(unmarshal)功能。

json.Marshal

json.Marshal函数实现Json的编码:

  • 如果给定的值不等于nil并且实现了json.Marshaler接口,调用json.Marshaler的MarshalJSON方法完成编码
  • 如果没有实现json.Marshaler接口,而是实现了encoding.TextMarshaler,将调用其MarshalText完成编码
  • 否则将按照下面的默认情况进行编码:
    • bool 编码为 JSON boolean
    • 整形,浮点型等数值 编码为 JSON number
    • string 编码为 JSON string
    • array和slice编码为JSON array, []byte作为例外会被编码成base64 string
    • map[string]interface{}编码为JSON object
    • struct编码为JSON object(只适用struct的可导出字段)
    • nil编码为JSON null

json tag定制struct编码

  • `json:“-”`忽略字段

    1
    2
    3
    4
    5
    
     struct {
            Name string `json:"-"`
            No   int
        }{"Hello", 112}
    // {"No":112}
  • `json:“myName”`制定字段名称

    1
    2
    3
    4
    5
    
    struct {
            Name string `json:"myName"`
            No   int
        }{"Hello", 112}
    // {"myName":"Hello","No":112}
  • `json:“,omitempty”`或`json:“myName,omitempty”`忽略空值字段

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    struct {
        Name string `json:"myName,omitempty"`
        No   int
    }{"Hello", 112}
    // {"myName":"Hello","No":112}
        
    struct {
        Name string `json:"myName,omitempty"`
        No   int
    }{}
    // {"No":0}

    关于空值的一个例子

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    foo := struct {
        F1 bool
        F2 int
        F3 float64
        F4 string
        F5 interface{}
        F6 *int
        F7 [2]int
        F8 []int
        F9 map[string]interface{}
    }{}
    buf, err := json.MarshalIndent(foo, "", "\t")
        
    编码结果为:
    {
    "F1": false,
    "F2": 0,
    "F3": 0,
    "F4": "",
    "F5": null,
    "F6": null,
    "F7": [
        0,
        0
    ],
    "F8": null,
    "F9": null
    }
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    foo := struct {
        F1 bool                   `json:",omitempty"`
        F2 int                    `json:",omitempty"`
        F3 float64                `json:",omitempty"`
        F4 string                 `json:",omitempty"`
        F5 interface{}            `json:",omitempty"`
        F6 *int                   `json:",omitempty"`
        F7 [2]int                 `json:",omitempty"`
        F8 []int                  `json:",omitempty"`
        F9 map[string]interface{} `json:",omitempty"`
    }{}
    buf, err := json.MarshalIndent(foo, "", "\t")
        
    编码结果为:
    {
    "F7": [
        0,
        0
    ]
    }

    json.MarshalIndent编码后的json格式上适合阅读

  • `json:“,string”`

    1
    2
    3
    4
    5
    
    struct {
        Name string
        No   int `json:",string"`
    }{"Hello", 112}
    // {"Name":"Hello","No":"112"}
  • 内嵌匿名结构会自动展开,可使用`json:“myName”`取消自动展开

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    type Foo struct {
        Name string
    }
    type Bar struct {
        Foo
        No int
    }
    v := Bar{Foo{"Hello"}, 112}
    // {"Name":"Hello","No":112}
        
    type Foo struct {
        Name string
    }
    type Bar struct {
        Foo `json:"Foo"`
        No  int
    }
    v := Bar{Foo{"Hello"}, 112}
    // {"Foo":{"Name":"Hello"},"No":112}

json.Unmarshal

json.Unmarshal函数实现json的解码。 func Unmarshal(data []byte, v interface{}) error v必须为指针。

json.RawMessage

json.RawMessage此类型的定义为type RawMessage []byte,它还实现了json.Marshaler和json.Unmarshaler接口,可以json的延迟编解码。这里引用官方文档中的例子,简单明了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main

import (
	"encoding/json"
	"fmt"
	"log"
)

func main() {
	type Color struct {
		Space string
		Point json.RawMessage // delay parsing until we know the color space
	}
	type RGB struct {
		R uint8
		G uint8
		B uint8
	}
	type YCbCr struct {
		Y  uint8
		Cb int8
		Cr int8
	}

	var j = []byte(`[
		{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
		{"Space": "RGB",   "Point": {"R": 98, "G": 218, "B": 255}}
	]`)
	var colors []Color
	err := json.Unmarshal(j, &colors)
	if err != nil {
		log.Fatalln("error:", err)
	}

	for _, c := range colors {
		var dst interface{}
		switch c.Space {
		case "RGB":
			dst = new(RGB)
		case "YCbCr":
			dst = new(YCbCr)
		}
		err := json.Unmarshal(c.Point, dst)
		if err != nil {
			log.Fatalln("error:", err)
		}
		fmt.Println(c.Space, dst)
	}
}

面向输入输出流的编解码器

json.Encoder和json.Decoder是面向输入输出流的编解码器,它们分别使用io.Reader和io.Writer进行创建,方便我们直接从输入流进行解码和直接编码输出到输出流中。

1
2
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder

第三方编解码库

因为标准库中json的编解码功能使用了Go的反射特性,因此如果对于性能比较敏感的程序,在github上有一些开源的选项,如https://github.com/pquerna/ffjsonhttps://github.com/mailru/easyjson,这些库一般都是基于go generate为struct生成MarshalJSON和UnmarshalJSON函数,避免了在编码和解码时对反射的调用。

参考