什么是json?
- json指的是JavaScript对象标记法(JavaScript Object Notation)
- json是一种轻量级的数据交换格式。
(数据格式以键值对的形式出现:name:value) - json具有自我描述性且易于理解。
- json独立于编程语言。
(json实际上是一个字符串,几乎所有语言都可以处理字符串数据)
因此在WEB开发过程中,使用json格式承载http信息十分方便,不用考虑发送方和接收方的语言差异。
虽然json只是一个字符串,但是其数据也是有数据类型之分的。对于格式转换仍然需要数据类型的映射。对于不同语言的语言特性,json会有不同的数据类型映射关系。
golang与json的数据类型映射
golang->json
golang | json |
---|---|
bool | Boolean |
int、float等数字类型 | Number |
string | String |
[]byte(base64编码) | String |
struct | Object,递归打包 |
array/slice | Array |
map | Object |
interface{} | 按实际情况转换 |
nil | null |
channel、func | UnsupportedTypeError |
json->golang
json | golang |
---|---|
Boolean | bool |
Number | int、float等数字类型 |
String | string |
Array | []interface{} |
Object | map[string]interface{} |
null | nil |
golang的json编码
最常用的编码、解码方法有两种:
结构体定义
type MallDetailForSave struct {
ID uint64 `json:"id"`
Name string `json:"name"`
Limit map[int]int64 `json:"limit"`
SpecID []uint32 `json:"spec_id"`
SpecTime TimeLimit `json:"spec_time"`
Sort int `json:"sort"`
}
type TimeLimit struct {
Start int `json:"start"`
End int `json:"end"`
}
json.Marshal()进行编码
func TestMarshal(t *testing.T) {
mall := &MallDetailForSave{}
mall.ID = 123
mall.Name = "123"
mall.Limit = make(map[int]int64, 0)
mall.Limit[1] = 7
mall.Limit[2] = 8
mall.Limit[3] = 9
mall.Limit[4] = 10
mall.SpecID = make([]uint32, 1)
mall.SpecID[0] = 77596687
mall.SpecTime = &TimeLimit{
Start: 1,
End: 2,
}
mall.Sort = 1
body, err := json.Marshal(&mall)
if err != nil {
fmt.Printf("err=%+v\n", err)
return
}
fmt.Printf("body=%s\n", string(body))
}
=== RUN TestMarshal
body={"id":123,"name":"123","limit":{"1":7,"2":8,"3":9,"4":10},"spec_id":[77596687],"spec_time":{"start":1,"end":2},"sort":1}
--- PASS: TestMarshal (0.00s)
PASS
json.NewEncoder()进行编码
func TestNewencoder(t *testing.T) {
mall := &MallDetailForSave{}
mall.ID = 123
mall.Name = "123"
mall.Limit = make(map[int]int64, 0)
mall.Limit[1] = 7
mall.Limit[2] = 8
mall.Limit[3] = 9
mall.Limit[4] = 10
mall.SpecID = make([]uint32, 1)
mall.SpecID[0] = 77596687
mall.SpecTime = &TimeLimit{
Start: 1,
End: 2,
}
mall.Sort = 1
buffer := &bytes.Buffer{}
err := json.NewEncoder(buffer).Encode(mall)
if err != nil {
fmt.Printf("err=%+v\n", err)
return
}
fmt.Printf("body=%s\n", buffer.String())
}
=== RUN TestNewencoder
body={"id":123,"name":"123","limit":{"1":7,"2":8,"3":9,"4":10},"spec_id":[77596687],"spec_time":{"start":1,"end":2},"sort":1}
--- PASS: TestNewencoder (0.00s)
PASS
golang的json解码
对应的,常用解码方式也有两种:
json.Unmarshal()进行解码
func TestUnmarshal(t *testing.T) {
mall := &MallDetailForSave{}
body := "{\"id\":123,\"name\":\"123\",\"point\":100,\"limit\":{\"1\":7,\"2\":8,\"3\":9,\"4\":10},\"spec_id\":[77596687],\"spec_time\":{\"start\":1,\"end\":2},\"sort\":1,\"gtype\":0}\n"
err := json.Unmarshal([]byte(body), &mall)
if err != nil {
fmt.Printf("err=%+v\n", err)
return
}
fmt.Printf("mall=%+v\n", mall)
}
=== RUN TestUnmarshal
mall=&{ID:123 Name:123 Limit:map[1:7 2:8 3:9 4:10] SpecID:[77596687] SpecTime:{Start:1 End:2} Sort:1}
--- PASS: TestUnmarshal (0.00s)
PASS
我们可以观察到:在字符串body中,并没有严格按照结构体的字段属性定义,而是多了两个字段,但是这样并不影响解码操作。因此在json解码过程中,解码器只会寻找可以匹配上的键值对,无法匹配的自动丢弃。这样极大地增加了解码的灵活性,不计个数,不计顺序。
json.TestUnnewdecoder()进行解码
func TestUnnewdecoder(t *testing.T) {
mall := &MallDetailForSave{}
body := "{\"id\":123,\"name\":\"123\",\"point\":100,\"limit\":{\"1\":7,\"2\":8,\"3\":9,\"4\":10},\"spec_id\":[77596687],\"spec_time\":{\"start\":1,\"end\":2},\"sort\":1,\"gtype\":0}\n"
err := json.NewDecoder(strings.NewReader(body)).Decode(&mall)
if err != nil {
fmt.Printf("err=%+v\n", err)
return
}
fmt.Printf("mall=%+v\n", mall)
}
=== RUN TestUnnewdecoder
mall=&{ID:123 Name:123 Limit:map[1:7 2:8 3:9 4:10] SpecID:[77596687] SpecTime:{Start:1 End:2} Sort:1}
--- PASS: TestUnnewdecoder (0.00s)
PASS
总结
两种方法的区别
- json.NewDecoder是从一个流里面直接进行解码,代码精干;
- json.Unmarshal是从已存在与内存中的json进行解码;
- 相对于解码,json.NewEncoder进行大JSON的编码比json.marshal性能高,因为内部使用pool。
使用场景
- json.NewDecoder用于http连接与socket连接的读取与写入,或者文件读取;
- json.Unmarshal用于直接是byte的输入。