JSON及序列化知识介绍
说到json,其实我们不得不说的是序列化,因为json就是序列化的一种协议。
序列化的目的是为了对象可以跨平台存储和进行网络传输,而网络传输是需要二进制字节这个形式的。那么在进行网络传输之前,就需要把对象转换成二进制字节,这个过程就是序列化的过程。
在使用方将对象转成二进制字节及接收方收到后将字节恢复的过程中,就需要两方规定好转换的规则,而这种规则就是所谓的协议。(说句题外话,之前看一种说法说盗墓笔记里面的胡八一为什么按照风水学可以找到大墓呢?是因为埋的人按照这套风水学埋的,所以盗墓的人按照这个来挖,自然就可以找得到。换到这里来说,埋的人用json这种规则来埋,你盗墓如果用Protobuf这种规则的话,自然是不成的)。
常见的序列化协议有json、xml、Protobuf、Thrift。之间区别等我再开一篇文章来说道说道。我们这次不谈屁股。
GO中JSON使用
一般来讲json串的反序列化都会解析成一个结构体,所以这里有一个区分是:一类是我实现知道会反序列化成什么样子的结构体,还有一类就是我事先不知道是什么样子的结构体,这一类会用在比较通用的地方:
明确结构体的json解析:
type exampleStruct struct {
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
}
var exampleObject exampleStruct
err := json.Unmarshal([]byte(jsonStr), &exampleObject)
if err != nil {
}
这个是我们比较常用的形式;
不确定结构体的json解析
这种在反序列化的时候,因为没有确定结构体,所以会以map[string]interface{}来充当结构体的角色。这个也很好理解,key-value形式一般key为string格式,而value则不确定。
exampleMap := make(map[string]interface{}, 0)
err := json.Unmarshal([]byte(jsonStr), &exampleMap)
err!=nil{
}
这种形式其实相对第一种比较不常用,使用的时候会在一些更通用或者说抽象的地方来使用。例如:在gin框架中,会观察到:
func (self *HttpServer) ping(context *gin.Context) {
context.JSON(200, gin.H{
"message": "pong",
})
}
就是这样来使用的,在gin.H这里我们需要传入的是一个结构体,但是这个比较通用一点,为了适用大多情况,这里用一个map[string]interface{}的形式来代替一个具体的结构体。
这种序列化给我们带来了便利,但是在反序列化的时候,这种通用的形式使用起来就很难受,说到底它还是一个map,在我们使用map的时候需要判断key是否存在,同时这里的value是any形式,在使用的过程中就需要类型判定。
value, ok := exampleMap["exampleKey"]; ok {
valueR := value.(string)
}
结构体json的Tag作用:
可以看到结构体中json是在序列化及反序列化中不可缺少的一大部分,这里是通过反射来实现字段和tag的绑定关系的,在接下来的浅析源码中会对这一部分进行深入的探寻。
结构体标签三个注意点:
1)结构体里面嵌套结构体
这种情况是,并不能保证在结构体里面的字段都是基础类型,结构体中包含结构体也是很常见的,这时候我们需要怎么定义呢?
type School struct {
Name string `json:"name"`
Address string `json:"address"`
Grade json.RawMessage `json:"grade"`
}
type Grade struct {
Class string `json:"class"`
Students string `json:"students"`
}
这时候我们就需要用到json.RawMessage这个类型了
其实json.RawMessage也没什么特别的也就是[]byte
2)结构体中忽略不存在的
type School struct {
Name string `json:"name"`
Address string `json:"address"`
Students string `json:"-"`
privateFiled string `json:"privateFiled"`
}
func main() {
jsonString := `{"name":"name","address":"shanghai","addFields":"321"}`
var schoolExample School
err := json.Unmarshal([]byte(jsonString), &schoolExample)
if err != nil {
fmt.Println(err.Error())
return
}
s := School{
Name: "name",
Address: "address",
Students: "students",
privateFiled: "privateFiled",
}
sByte, err := json.Marshal(s)
fmt.Println(string(sByte))
//{"name":"name","address":"address"}
}
这种在json:"-"的形式,其实在反序列化的时候作用不大,在序列化的时候:这种保证了某些值不会被序列化传到外边去(起到一个数据保护的作用)
3)私有值
还有一点就是privateFiled这个字段,它的首字母是小写的证明了这个是一个一个私有变量,这个在序列化中其实和json:"-"的形式的作用没有差别。这里特别拉出来说是是为了给源码解析做个铺垫。