Golang | json

_Liu_ · · 709 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

一、解析JSON

1、解析JSON有两种方法
  • 解析到结构体中(需要提前知道结构体的结构);
  • 解析到接口;
2、JSON数据与struct字段是如何相匹配的
  • 首先查找tag(标签)中,含有Foo的可导出的struct字段(若标签不匹配,则匹配失败)
  • 其次查找,字段名是Foo的导出字段
  • 最后查找类似FOO或者FoO这样的除了首字母之外其他大小写不敏感的导出字段

注意:当接收到一个很大的JSON数据结构而你只想获取其中的部分数据的时候,只需将想要的数据对应的字段名大写,即可轻松解决这个问题。

3、匹配流程

tag——Foo——FOO或FoO或FOo

4、tag注意事项
  • 字段的tag是“-”,直接忽略该字段
  • tag中如果带有“omitempty”选项,那么如果该字段值为空,则忽略该字段(json编码时不会出现该字段)
  • 如果字段类型是bool,string,int,int64等,而tag中带有“,string”选项,那么这个字段在输出到JSON的时候会把该字段对应的值转换成JSON字符串
5、数据类型的对应
Bool                   对应JSON布尔类型
float64                对应JSON数字类型
string                 对应JSON字符串类型
[]interface{}          对应JSON数组  [1,2,3]
map[string]interface{} 对应JSON对象  {"name":"alan",age:18}
nil                    对应JSON的null
复制代码
6、RawMessage

RawMessage类型是一个保持原本编码的json对象。本类型实现了Marshaler和Unmarshaler接口,用于延迟json的解码或者预计算json的编码。

type RawMessage []byte
本质是个byte数组,相当于json解析时,并没有解析RawMessage类型的字段,所以说起到了延迟json解析的作用
复制代码

二、注意

1、变量首字母要大写,才可导出
type Student struct {
	Name   string `json:"name"`	//json标签
	Age    int    `json:"age"`
	School string `json:"school"`
	Class  int    `json:"class"`
}
对于不可导出的字段,在编码和解码结果中,均不会出现;

{"name":"Alan","age":20,"school":"gzhu","class":10}//这就是标签的作用
复制代码
2、解码任意的数据
var f interface{}
err := json.Unmarshal(b, &f)
m := f.(map[string]interface{})		//也可以直接用map[string]interface{}接收,则省略了这步转化

for k, v := range m {//通过 for range 语法和 type switch 来访问其实际类型:
    switch vv := v.(type) {
        case string:
        	fmt.Println(k, "is string", vv)
        case int:
        	fmt.Println(k, "is int", vv)
        case []interface{}:
            fmt.Println(k, "is an array:")
            for i, u := range vv {
                fmt.Println(i, u)
            }
        default:
        	fmt.Println(k, "is of a type I don’t know how to handle")
        }
}
复制代码
3、数字默认转化为float64

由于 JSON 里的数字默认都会转成 Golang 的 float64 类型

4、性能问题

Go 语言里面原生支持了json的序列化以及反序列化,内部使用反射机制实现,性能有点差,在高度依赖 json 解析的应用里,往往会成为性能瓶颈。这时可以使用性能较好的第三方库;听说后续版本已经做了性能优化

5、编码指针类型
s := &Student{
    Name:   "Alan",
    Age:    20,
    School: "gzhu",
    Class:  10,
}
data, err := json.Marshal(s)
//这样处理是合法的,结果也是正确的。
//遇到指针时,golang会自动的递归下降的进行格式转换。
复制代码

三、性能优良的第三方json包

package main

import (
	"fmt"
	//包别名,这样用起来跟系统包似的,代码都不用改了
	json "github.com/json-iterator/go"
)

func main() {
	val := []byte(`{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}`)
	m := make(map[string]string)
	json.Unmarshal(val, &m)//号称100%兼容标准库的json
	fmt.Println(m)

	//不用全部解析,单独取出某个字段
	str := json.Get(val, "Colors", 0).ToString()
	fmt.Println(str)
}
复制代码

四、解码中的一些坑

1、code不同,Data的数据类型也不同;
type CheckIPResult struct {
	Code int             `json:"code"`
	Msg  string          `json:"msg"`
	// Data interface{} `json:"data"`
	Data json.RawMessage `json:"data"`
}

code != 0 {"msg": "miss param", "code": -1, "data": ""} //data是个空string
code = 0 {"msg": "", "code": 0, "data": {"123.163.183.200:18635": false, "27.209.164.24:15718": false}}

此例中,code的值决定了data字段的类型;
所以data需设计成interface{}类型,可接受任意类型;
或设计成json.RawMessage,延迟解析。先解析code,根据code的类型进一步解析json.RawMessage
复制代码
// CheckIPResult 测试IP结果
type CheckIPResult struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	//Data interface{} `json:"data"`
	Data json.RawMessage `json:"data"`
}

func main() {
	resp := `
	{
		"msg": "", 
		"code": 0, 
		"data": {"123.163.183.200:18635": false, "27.209.164.24:15718": false}
	}
	`
	// resp = `{"msg": "miss param", "code": -1, "data": ""}`
	data := new(CheckIPResult)
	err := json.Unmarshal([]byte(resp), &data)
	if err != nil {
		fmt.Println("json unmarshal failed,err:", err)
		return
	}

	if data.Code == 0 {
		//===========interface{}==============
		//d, ok := data.Data.(map[string]interface{})
		//		//if !ok {
		//		//	fmt.Println("类型转换失败")
		//		//}else{
		//		//	fmt.Println(d)
		//		//}

		//===============json.RawMessage================
		d := make(map[string]bool)
		err := json.Unmarshal(data.Data, &d)
		if err != nil {
			fmt.Println("json unmarshal failed,err:", err)
			return
		}
		fmt.Println(d)
	} else {
		//===========interface{}==============
		//d, ok := data.Data.(string)
		//if !ok {
		//	fmt.Println("类型转换失败")
		//}else{
		//	fmt.Println(d)
		//}

		//===============json.RawMessage================
		d := ""
		err := json.Unmarshal(data.Data, &d)
		if err != nil {
			fmt.Println("json unmarshal failed,err:", err)
			return
		}
		fmt.Println(d)
	}
}
复制代码
2、interface与具体类型的比较
var i interface{}
i = true
fmt.Println(i == true) //true

可将具体类型的值赋给interface,且此时的interface可以和具体来行就行比较;
反之,则不能将interface赋给具体类型
复制代码

有疑问加站长微信联系(非本文作者)

本文来自:掘金

感谢作者:_Liu_

查看原文:Golang | json

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

709 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传