Go语言:如何方便地提取Json字段?

简述

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到了吗?

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 简介 什么是 Google Protocol Buffer? 假如您在网上搜索,应该会得到类似这样的文字介绍: G...
    Ucan先生阅读 1,140评论 0 0
  • 微服务架构已成为目前互联网架构的趋势,关于微服务的讨论,几乎占据了各种技术大会的绝大多数版面。国内使用最多的服务治...
    阿斯蒂芬2阅读 6,121评论 0 2
  • 前言 在上一篇 文章 中已经介绍了 枚举 类型字段的使用,本文接着介绍 JSON 类型字段的使用。 关于 JSON...
    anyesu阅读 5,866评论 3 1
  • 1. Json格式 完整的一条json语句中,字段都为字符串类型,值为基本数据类型:整形、布尔型、字符串等 2. ...
    阿斯巴甜不太甜阅读 908评论 0 0
  • 有的时候,用于序列化、反序列化的类型中字段的名称,和JSON字符串中的字段的名称可能不是规范的映射关系,这个时候我...
    CokeCode阅读 7,810评论 0 0