json在Go中简介
1. 编码与解码
func Marshal(v interface{}) ([]byte, error)
func Unmarshal(data []byte, v interface{}) error
1.1. 示例
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
50
| package main
import (
"encoding/json"
"fmt"
"log"
)
type Goods struct {
Id int
Name string
Producer []string
RealPrice float64 `json:"price"`
SalePrice float64
Price float64
}
func main() {
// goods json
gb, err := GoodsJson()
if err != nil {
log.Panic(err)
}
// goods object
g, err := GoodsObj(gb)
if err != nil {
log.Panic(err)
}
// main.Goods{Id:100, Name:"HEAD PHONE", Producer:[]string{"Huawei", "Xiaomi", "Meizhu"}, RealPrice:4300, SalePrice:0}
//
fmt.Printf("%#v", g)
}
// GoodsJson get goods json encode
func GoodsJson() (jsbyte []byte, err error) {
goods := map[string]interface{}{
"id": 100,
"name": "HEAD PHONE",
"producer": []string{"Huawei", "Xiaomi", "Meizhu"},
"price": 4300.00,
}
return json.Marshal(goods)
}
// GoodsObj get goods object
func GoodsObj(jsbyte []byte) (g Goods, err error) {
return g, json.Unmarshal(jsbyte, &g)
}
|
1.2. 编码
只有可以表示为有效JSON的数据结构才会被编码:
- 仅支持字符串作为key,编码go的map类型,必须形式为
map[string]T
,且T类型也是可以被json编码的 - json包只访问struct类型的导出字段(以大写字母开头的字段),因此,只有结构的导出字段才会出现在JSON输出中(注意编码和解码都是如是)
- Channel, complex, function类型不能被编码
- 不支持循环数据结构,会导致json编码过程的无限循环
- 指针将被编码为它们指向的值(如果指针为零,则为“null”)
1.3. 解码
Unmarshal如何识别存储解码数据的字段?
对于给定的JSON键“Foo”,Unmarshal将查看目标结构的字段以查找(按优先顺序):
- 导出字段,且有Json标记的(参见Exp的
json:"price"
); - 导出字段,字段为“Foo”;
- 导出字段,“FOO”或“FoO”的导出字段或“Foo”的其他一些不区分大小写的匹配项。
Unmarshal将仅解码它可以在目标类型中找到的字段,当从大型JSON lob中仅选择一些特定字段时,此行为特别有用。
它还意味着目标结构中的任何未导出的字段都不受Unmarshal的影响。
2. 其他问题
2.1. JSON与interface
json包使用map[string]interface{}
和[]interface{}
值来存储任意JSON对象和数组;它会愉快地将任何有效的JSON blob
解组为普通的**interface{}**值。默认的具体Go类型是:
- 用于JSON布尔值的bool
- 用于JSON数字的float64
- 用于JSON字符串的字符串
- 用于JSONnull的nil
2.2. 解析任意JSON数据到interface中
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
| func ParseJsonStr() {
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
var f interface{}
err := json.Unmarshal(b, &f)
if err != nil {
log.Panic(err)
}
m := f.(map[string]interface{})
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case float64:
fmt.Println(k, "is float64", 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")
}
}
}
|
2.3. JSON解析的到结构体中字段引用问题
考虑以下内容信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 切片引用
type FamilyMember struct {
Name string
Age int
Parents []string
}
var m FamilyMember
err := json.Unmarshal(b, &m)
// 对象引用
type Foo struct {
Bar *Bar
}
|
- 切片应用中,var定义m的时候,
m.Parents
为nil类型,但json.Unmarshal通过解析过程中创建新的切片来完成了解析 - 对象引用中,BarJSON对象中有一个字段,
Unmarshal
则会分配一个新字段Bar并填充它,如果没有,Bar将nil留作指针
由此可以引出一个有用的接收器设计模式:
1
2
3
4
| type IncomingMessage struct {
Cmd *Command
Msg *Message
}
|
如果您有一个接收几种不同消息类型的应用程序,发送方可以填充顶级JSON对象的Cmd字段和/或Msg字段,具体取决于想要通信的消息类型。
在将JSON解码为IncomingMessage结构时,Unmarshal将仅分配JSON数据中存在的数据结构。
要知道要处理哪些消息,程序员只需测试Cmd或Msg不是nil。
2.4. JSON流式编码和解码
json包提供解码器和编码器类型,以支持读写JSON数据流的常见操作。NewDecoder
和NewEncoder
函数包装了io.Reader
和io.Writer
接口类型。
- func NewDecoder(r io.Reader) *Decoder
- func NewEncoder(w io.Writer) *Encoder
示例程序,它从标准输入读取一系列JSON对象,从每个对象中删除除Name字段之外的所有对象,然后将对象写入标准输出:
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
| func StreamJsonHandle() {
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
}
fmt.Println(v)
for k := range v {
if k != "Name" {
delete(v, k)
}
}
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}
}
// input
// {"id":70,"Name":"Cpu","price":2000.88,"producer":["Intel","AMD"]}
// output
// map[Name:Cpu price:2000.88 producer:[Intel AMD] id:70]
// {"Name":"Cpu"}
|
由于读/写器无处不在,这些编码器和解码器类型可用于各种场景,例如读取和写入HTTP连接,WebSockets或文件。