十分钟教你写一个 Json 解析库
fpdk4226
8年前
<p>json是在开发当中一种常见的数据结构,本身它的协议栈就是很简洁的,如果有兴趣的同学,可以参考RFC4627文档,本文的最后我会附上这部分的链接,方便大家查看。那么在go项目的开发当中,比如最常见的web项目,我们需用到json来传递数据,那么必然就会面对将json object,转换成go中对应的数据结构的需求。无需担心的是,go提供了encoding/json解析包,我们接下来要做的工作就是在此基础上做一定封装,以方便自己使用。同时这个库也是特别适合新手来练手使用,因为它设计到了go中的map,struct,assert,interface{}...,所以对于加深对go基础的理解也很有帮助,甚至你还可以写出更好的呢!</p> <p>项目地址: <a href="/misc/goto?guid=4959729859074905609" rel="nofollow,noindex">https://github.com/liyu4/ljson</a></p> <h2>开始</h2> <p>定义一个全局对象,data为interface{}类型意味这可以存储任何类型的数据。</p> <pre> <code class="language-go">typeJs struct { datainterface{} } </code></pre> <p>工厂模式实例化Js对象,这里我们将需要反序列化的data数据,先转换成byte数组, 然后将其解析到&f指向的值中,也就是说f是包含了我们反序列化得到的数据。本文接下来的部分就是对这个f的操作。</p> <pre> <code class="language-go">funcNewJson(datastring) *Js { j:= new(Js) varfinterface{} err:=json.Unmarshal([]byte(data), &f) if nil !=err{ returnj } j.data=f returnj } </code></pre> <p>获取map,这里我们使用了断言,判断interface{}是否是map[string]interface{}类型</p> <pre> <code class="language-go">// return map in Go func(j*Js) GetMapData()map[string]interface{} { ifm,ok:= (j.data).(map[string]interface{});ok{ returnm } return nil } </code></pre> <p>获取key对应的value</p> <pre> <code class="language-go">// Acoording to the key of the returned data information , return js.data // if you know json is an object func(j*Js) Get(keystring) *Js { m:=j.GetMapData() ifv,ok:=m[key];ok{ j.data=v returnj } j.data= nil returnj } </code></pre> <p>或者value根据下标,这里指的是从slice和map中,你懂的,这里有一个点需要小心一下,就是map返回的顺序是不固定的</p> <pre> <code class="language-go">// GetIndex get []interface or map in Go func(j*Js) GetIndex(iint) *Js { num:=i- 1 ifm,ok:= (j.data).([]interface{});ok{ ifnum<=len(m)-1{ v:=m[num] j.data=v }else{ j.data= nil } returnj } { ifm,ok:= (j.data).(map[string]interface{});ok{ varn= 0 vardata=make(map[string]interface{}) fori,v:=range m{ ifn==num{ switchvv:=v.(type) { casefloat64: data[i] =strconv.FormatFloat(vv, 'f', -1, 64) j.data=data returnj case string: data[i] =vv j.data=data returnj case []interface{}: j.data=vv returnj } } n++ } } } j.data= nil returnj } </code></pre> <p>自定义key和下标返回value,注意错误的处理</p> <pre> <code class="language-go">// The data must be []interface{}, According to your custom number to return key adn array data func(j*Js) GetKey(keystring,iint) (*Js,error) { num:=i- 1 ifi>len((j.data).([]interface{})) { return nil,errors.New("index out of range list") } ifm,ok:= (j.data).([]interface{});ok{ v:=m[num].(map[string]interface{}) ifh,ok:=v[key];ok{ j.data=h returnj, nil } } j.data= nil returnj, nil } </code></pre> <p>递归map</p> <pre> <code class="language-go">// According to the custom of the PATH to fing element // You can use function this to find recursive map func(j*Js) GetPath(args...string) *Js { d:=j fori:=range args{ m:=d.GetMapData() ifval,ok:=m[args[i]];ok{ d.data=val } else { d.data= nil returnd } } returnd } </code></pre> <p>String方法,相信大家也看到了我们的slice和map中的值都是interface{},转换陈string能方便我们的操作。</p> <pre> <code class="language-go">// String return string func(j*Js) String() string { ifm,ok:=j.data.(string);ok{ returnm } ifm,ok:=j.data.(float64);ok{ returnstrconv.FormatFloat(m, 'f', -1, 64) } return "" } </code></pre> <p>返回key和value数组 []string</p> <pre> <code class="language-go">func(j*Js) ToArray() (k,d[]string) { varkey,data[]string ifm,ok:= (j.data).([]interface{});ok{ for_,value:=range m{ forindex,v:=range value.(map[string]interface{}) { switchvv:=v.(type) { casefloat64: data=append(data,strconv.FormatFloat(vv, 'f', -1, 64)) key=append(key,index) case string: data=append(data,vv) key=append(key,index) } } } returnkey,data } ifm,ok:= (j.data).(map[string]interface{});ok{ forindex,v:=range m{ switchvv:=v.(type) { casefloat64: data=append(data,strconv.FormatFloat(vv, 'f', -1, 64)) key=append(key,index) case string: data=append(data,vv) key=append(key,index) } } returnkey,data } return nil, nil } </code></pre> <p>返回一个[]string</p> <pre> <code class="language-go">// Array return array func(j*Js) Array() ([]string,error) { ifa,ok:= (j.data).([]interface{});ok{ array:=make([]string, 0) for_,v:=range a{ switchvv:=v.(type) { casefloat64: array=append(array,strconv.FormatFloat(vv, 'f', -1, 64)) case string: array=append(array,vv) } } returnarray, nil } return nil,errors.New("type assertion to []interface{} failed") } </code></pre> <p>一个漂亮但是没啥子用的调试函数</p> <pre> <code class="language-go">//for test func(j*Js) Type() { fmt.Println(reflect.TypeOf(j.data)) } </code></pre> <p>至此我们就完成了一个可以部署在生产环境中的json library,这个项目是小巧和坚实的,希望大家能自己动手写写,最好是在下雨的星期天下午。</p> <h2>参考</h2> <p><a href="/misc/goto?guid=4959729859181179201" rel="nofollow,noindex">RFC4627</a> <a href="/misc/goto?guid=4959729859262894630" rel="nofollow,noindex">Json in Go</a></p> <p> </p> <p>来自:https://www.zybuluo.com/aliasliyu4/note/601706</p> <p> </p>