简述
Json是http/https协议中十分常见的报文格式之一,以其简单明了的特性,深受前后端工程师的青睐。由于前端通常采用javascript解析后端发送过来的json数据,而javascript是动态语言,所以解析json变得很容易。后端的技术栈种类繁多,可以使用动态语言,例如python/ruby等,更多情况下会采用静态编译型语言,像Java/C#/Go等。
反序列化
1、动态语言的Json反序列化
通常反序列化为通用类型,比如字典,object对象等,由于动态语言编写时,不会校验类型,所以可以动态地获取字段,举一个Python的例子:
import json
j_str = '{"header":{"user":"admin"}}'
d = json.loads(j_str)
header = d["header"]
从上面的例子可以看出json模块将字符串反序化成动态字典,可以很方便地获取字典的key,但也有弊端,至少有两点:1)性能低 2)很可能有运行时异常(假设前端或者移动端发过来的报文格式变了)
2、静态语言
反序列化得到的类型,一般有两种形式:一种是强类型的class,一种是通用类型,例如Java中的Map<String, Object>类型。反序列化的库一般都会采用泛型方法支持这两种情况。
这两种方式都各有优缺点:
1)class类型
优点:一旦反序列化后,获取读取Json的数据,很方便。
缺点:如果报文五花八门,需要创建很多这样的类来对应不一样的报文格式。
2)Map类型
优点:不需要额外创建class类型来匹配不同的报文格式
缺点:读取json数据的时候,每个object可能都需要做强制类型转换,代码会十分难看,且效率低下
两种方式均有在采用,个人认为可能使用class的情况似乎更好一些。
Go: Json反序列化
Go是静态编译型语言,所以以上两种方式都是支持:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
jStr := `{"name":"xiaoming","age":18}`
var p Person
err := json.Unmarshal([]byte(jStr), &p)
if err == nil{
fmt.Print(p.Name, p.Age)
}
var m map[string]interface{}
err := json.Unmarshal(jsonBytes, &m)
当然优缺点也如上面陈述的一样。
那么如何方便地获取数据呢?
我们想到一种方式: 如果能像python一样轻松获取,还不失去作为静态语言的性能优势。那是不是就能起飞了,于是先想到怎么使用,再来阐述如何实现,我希望这样使用:
jsonStr := `{
"links": {
"self": "/domains/test.one"
},
"data": {
"type": "domains",
"id": "test.one",
"attributes": {
"product": " Website",
"package": "Professional",
"created_at": "2016-08-19T11:37:01Z"
}
}
}`
m, _ := types.ConvJson2Map([]byte(jsonStr))
fmt.Println(m.Get("data").Get("Type").Value())
是不是和python很相似。那么我们就要实现一个函数:ConvJson2Map,返回的对象支持Get方法。
func ConvJson2Map(jsonBytes []byte) (*KeyStringMap, error){
}
返回类型的接口,假设叫IJsonValue,除了支持Get,还有其他方法:
package types
type IJsonValue interface {
Get(key string) IJsonValue
DeepGet(key string) IJsonValue
ContainsKey(key string) bool
GetAt(index int) IJsonValue
Contains(v interface{}) bool
Value() interface{}
IsMap() bool
IsSliceOrArray() bool
}
那么我们只要实现ConvJson2Map方法,似乎就能把我们的想法落地了。实现过程是一些类型转换,遍历算法等,就不赘述了。直接贴代码。
package types
import (
"encoding/json"
"reflect"
)
type KeyStringMap struct {
innerMap map[string]interface{}
}
type KSMapEntry struct {
Key interface{}
Value interface{}
}
type JsonValue struct {
value interface{}
}
func (j JsonValue) IsMap() bool {
return reflect.TypeOf(j.value).Kind() == reflect.Map
}
func (j JsonValue) IsSliceOrArray() bool {
return reflect.TypeOf(j.value).Kind() == reflect.Slice || reflect.TypeOf(j.value).Kind() == reflect.Array
}
func (j JsonValue) Get(key string) IJsonValue {
if j.IsMap() {
m := ConvKSMap(j.value)
if vv := m.Get(key); vv != nil {
return JsonValue{value: vv.Value()}
} else {
return nil
}
}
return nil
}
func deepGet(j IJsonValue, key string) IJsonValue {
if v := j.Get(key); v != nil {
return v
} else {
m := ConvKSMap(j.Value())
values := m.Values()
maps := values.Filter(func(v interface{}) bool {
return reflect.TypeOf(v).Kind() == reflect.Map
})
if maps.Len() == 0 {
return nil
} else {
for i := 0; i < maps.Len(); i++ {
jj := JsonValue{value: maps.GetAt(i)}
rr := deepGet(jj, key)
if rr != nil && rr.Value() != nil {
return rr
}
}
}
}
return nil
}
func (j JsonValue) DeepGet(key string) IJsonValue {
if j.IsMap() {
return deepGet(j, key)
}
return nil
}
func (j JsonValue) GetAt(index int) IJsonValue {
if j.IsSliceOrArray() {
m := ConvSlice(j.value)
vv := m.GetAt(index)
return JsonValue{value: vv}
}
return nil
}
func (j JsonValue) ContainsKey(key string) bool {
if j.IsMap() {
m := ConvKSMap(j.value)
return m.ContainsKey(key)
}
return false
}
func (j JsonValue) Contains(v interface{}) bool {
if j.IsSliceOrArray() {
s := ConvSlice(j.value)
return s.Contains(v)
}
return false
}
func (j JsonValue) Value() interface{} {
return j.value
}
func (m KeyStringMap) Get(key string) IJsonValue {
if v, ok := m.innerMap[key]; ok {
return JsonValue{value: v}
} else {
return nil
}
}
func mapDeepGet(m *KeyStringMap, key string) IJsonValue {
if v := m.Get(key); v != nil {
return v
} else {
values := m.Values()
maps := values.Filter(func(v interface{}) bool {
return reflect.TypeOf(v).Kind() == reflect.Map
})
if maps.Len() == 0 {
return nil
} else {
for i := 0; i < maps.Len(); i++ {
jj := ConvKSMap(maps.GetAt(i))
rr := mapDeepGet(jj, key)
if rr != nil && rr.Value() != nil {
return rr
}
}
}
}
return nil
}
func (m *KeyStringMap) DeepGet(key string) IJsonValue {
if v, ok := m.innerMap[key]; ok {
return JsonValue{value: v}
} else {
return mapDeepGet(m, key)
}
}
func (m *KeyStringMap) Set(key string, value interface{}) {
m.innerMap[key] = value
}
func (m KeyStringMap) ContainsKey(key string) bool {
if _, ok := m.innerMap[key]; ok {
return true
} else {
return false
}
}
func (m KeyStringMap) Keys() *Slice {
ret := make([]interface{}, 0)
for k, _ := range m.innerMap {
ret = append(ret, k)
}
return ConvSlice(ret)
}
func (m KeyStringMap) Values() *Slice {
ret := make([]interface{}, 0)
for _, v := range m.innerMap {
ret = append(ret, v)
}
return ConvSlice(ret)
}
func (m KeyStringMap) EntrySet() []KSMapEntry {
ret := make([]KSMapEntry, 0)
for k, v := range m.innerMap {
entry := KSMapEntry{Key: k, Value: v}
ret = append(ret, entry)
}
return ret
}
func (m KeyStringMap) ContainsValue(value interface{}) bool {
values := m.Values()
for _, e := range values.innerSlice {
if e == value {
return true
}
}
return false
}
func NewKSMap() *KeyStringMap {
m := make(map[string]interface{})
iMap := &KeyStringMap{innerMap: m}
return iMap
}
func ConvKSMap(m interface{}) *KeyStringMap {
iMap := &KeyStringMap{innerMap: m.(map[string]interface{})}
return iMap
}
func ConvJson2Map(jsonBytes []byte) (*KeyStringMap, error) {
m := NewKSMap()
err := json.Unmarshal(jsonBytes, &m.innerMap)
return m, err
}
至此我们就能像python一样方便去读json数据了。
聪明的你,Get到了吗?