Go Json

JSON(JavaScript Object Notations)是一种简单的数据交互格式,经常用于不同系统之间的信息交换。

Go语言中的encoding/json根据RFC 7159规范实现了JSON的编码解码。

编码

Go语言中的结构编码为Json

1
func Marshal(v interface{}) ([]byte, error)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"encoding/json"
"fmt"
)

type Message struct {
Name string
Body string
Time int64
}

func main() {
m := Message{"Alice", "Hello", 1294706395881547000}
b, _ := json.Marshal(m)
fmt.Printf(string(b))

}

//output
{"Name":"Alice","Body":"Hello","Time":1294706395881547000}

只有能被表达为有效的JSON格式的Go数据类型才能被正确的编码:

  • JSON对象只支持字符串作为键;被编码的结构体必须符合如下格式map[string]T(T为json package支持的任意Go类型)
    Channel,Complex,func不能被编码
    - 不支持循环的数据结构;会导致Marshal掉入无限循环
    - 如果数据类型是指针将会编码会指针指向的值(如果指针为nil刚编码为null)
  • json package只会处理结构体中以大写字母开头的字段
解码

JSON解码为Go语言中的结构体

1
func Unmarshal(data []byte, v interface{}) error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 package main

import (
"encoding/json"
"fmt"
)

type Message struct {
Name string
Body string
Time int64
}

func main() {
var m Message
b := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
json.Unmarshal(b, &m)
fmt.Println(m)
fmt.Println(m.Body)
}

Unmarshal是如何解码JSON中的数据,对于JSON的字段是如何对应结构体的字段?
假如JSON中有一个键为Foo
Unmarshal首先会寻找导出字段中有标签json:”Foo”的字段;如果不存在标签则会寻找导出字段``Foo;如果还未找到,则会寻找导出字段FOO,FoO或者其它忽略大小写的匹配。如果未找到匹配字段则会忽略。

解码任意结构的JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"encoding/json"
"fmt"
)


func main() {
var f interface{}
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)

json.Unmarshal(b, &f)

//此时并不能通过f.Name或f["Name]进行数据字段访问,需要通过如下语句进行类型转换,可以通过f.(type)判断f可以转换为什么类型
m := f.(map[string]interface{})

for k,v := range f {
fmt.Println(k, v)
}
}
Streaming Encoders and Decoders

json包也提供了DecoderEncoder类型来处理流式的JSON数据。

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

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
package main

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

func main() {
dec := json.NewDecoder(os.Stdin)
enc := json.NewEncoder(os.Stdout)
for {
var v map[string]interface{}
if err := dec.Decode(&v); err != nil {
log.Println(err)
return
}
for k := range v {
if k != "Name" {
delete(v, k)
}
}
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}
}

上面代码实现的功能是从标准输入读取数据,先进行解码,然后判断键如果不等于Name则删除,最后输出到标准输出。

struct field tags
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
// 编码为json时,字段Name名称会被改写为myName
// {"myName":"Alice","Body":"Hello","Time":1294706395881547000}
Name string `json:"myName"`

// 编码为json时,字段Name名称会被改写为myName
// 如果Name字段为空,则输出为{"Body":"Hello","Time":1294706395881547000},不包含myName字段
Name string `json: "myName,omitempty"`

// 编码为json时,字段Name名称不会被改写
// 如果Name字段为空,则输出为{"Body":"Hello","Time":1294706395881547000},不包含Name字段
Name string `json: ",omitempty"`

// 编码为json时,忽略字段Name
Name string `json: "-"`

// 编码为json时,将键Name替换为-
Name string `json:"-,"`

//string标签只用于string, float,integer,bool类型的字段

// 将int类型转换为字符串,输出为{"Time":"1294706395881547000"}
Time int64 `json:",string"`

// 将bool类型转换为字符串,输出为{"Bool":"true"}
Bool bool `json:",string"`

结构体嵌套

1
2
3
4
5
6
7
8
9
10
type Embedded struct {
F1 int
F2 string
}

// 在结构体中嵌套结构体Extra,需指定为inline
type Foo struct {
Name `json:"name"`
Embedded `json:",inline"`
}


Ref:
1.https://blog.golang.org/json
2.https://golang.org/pkg/encoding/json/
3.https://play.golang.org/p/NnwTh9KI5r