go语言json使用技巧

基本的序列化

在Go语言中json.Marshal()(系列化)与json.Unmarshal(反序列化)的基本用法

type Person struct {
	Name   string
	Age    int64
	Weight float64
}

func main() {
	p1 := Person{
		Name:   "奇奇",
		Age:    18,
		Weight: 55.5,
	}
	// struct -> json string
	b, err := json.Marshal(p1)
	if err != nil {
		fmt.Printf("json.Marshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)
	// json string -> struct
	var p2 Person
	err = json.Unmarshal(b, &p2)
	if err != nil {
		fmt.Printf("json.Unmarshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("p2:%#v\n", p2)
}

输出为

str:{"Name":"奇奇","Age":18,"Weight":55.5}
p2:main.Person{Name:"奇奇", Age:18, Weight:55.5}
tag的介绍以及用法

虽然平时很少用到,但是感觉还是很方便的样子,以后可以巧妙的应用
Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。
Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:

`key1:"value1" key2:"value2"`

结构体tag由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔。

  • 使用json tag指定字段名
    序列化与反序列化默认情况下使用结构体的字段名,我们可以通过给结构体字段添加tag来指定json序列化生成的字段名
    // 使用json tag指定序列化与反序列化时的行为
    type Person struct {
    	Name   string `json:"name"` // 指定json序列化/反序列化时使用小写name
    	Age    int64
    	Weight float64
    }
    
  • 忽略某个字段
    如果你想在json序列化/反序列化的时候忽略掉结构体中的某个字段,可以按如下方式在tag中添加-
    // 使用json tag指定json序列化与反序列化时的行为
    type Person struct {
    	Name   string `json:"name"` // 指定json序列化/反序列化时使用小写name
    	Age    int64
    	Weight float64 `json:"-"` // 指定json序列化/反序列化时忽略此字段
    }
    
  • 忽略空值字段
    当 struct 中的字段没有值时, json.Marshal() 序列化的时候不会忽略这些字段,而是默认输出字段的类型零值(例如int和float类型零值是 0,string类型零值是"",对象类型零值是 nil)。如果想要在序列序列化时忽略这些没有值的字段时,可以在对应字段添加omitempty tag。
    在举个栗子吧:
    type User struct {
    	Name  string   `json:"name"`
    	Email string   `json:"email"`
    	Hobby []string `json:"hobby"`
    }
    
    func omitemptyDemo() {
    	u1 := User{
    		Name: "奇奇",
    	}
    	// struct -> json string
    	b, err := json.Marshal(u1)
    	if err != nil {
    		fmt.Printf("json.Marshal failed, err:%v\n", err)
    		return
    	}
    	fmt.Printf("str:%s\n", b)
    }
    
    再来看看输出的结果
    str:{"name":"七米","email":"","hobby":null}
    
    如果想要在最终的序列化结果中去掉空值字段,可以像下面这样定义结构体
    // 在tag中添加omitempty忽略空值
    // 注意这里 hobby,omitempty 合起来是json tag值,中间用英文逗号分隔
    type User struct {
    	Name  string   `json:"name"`
    	Email string   `json:"email,omitempty"`
    	Hobby []string `json:"hobby,omitempty"`
    }
    
    来来来,看效果了
    str:{"name":"奇奇"} // 序列化结果中没有email和hobby字段
    
  • 首先来看几种结构体嵌套的示例:
    type User struct {
    	Name  string   `json:"name"`
    	Email string   `json:"email,omitempty"`
    	Hobby []string `json:"hobby,omitempty"`
    	Profile
    }
    
    type Profile struct {
    	Website string `json:"site"`
    	Slogan  string `json:"slogan"`
    }
    
    func nestedStructDemo() {
    	u1 := User{
    		Name:  "七米",
    		Hobby: []string{"足球", "双色球"},
    	}
    	b, err := json.Marshal(u1)
    	if err != nil {
    		fmt.Printf("json.Marshal failed, err:%v\n", err)
    		return
    	}
    	fmt.Printf("str:%s\n", b)
    }
    
    匿名嵌套Profile时序列化后的json串为单层的:
    str:{"name":"七米","hobby":["足球","双色球"],"site":"","slogan":""}
    
    想要变成嵌套的json串,需要改为具名嵌套或定义字段tag:
    type User struct {
    	Name    string   `json:"name"`
    	Email   string   `json:"email,omitempty"`
    	Hobby   []string `json:"hobby,omitempty"`
    	Profile `json:"profile"`
    }
    // str:{"name":"七米","hobby":["足球","双色球"],"profile":{"site":"","slogan":""}}
    
    想要在嵌套的结构体为空值时,忽略该字段,仅添加omitempty是不够的:
    type User struct {
    	Name     string   `json:"name"`
    	Email    string   `json:"email,omitempty"`
    	Hobby    []string `json:"hobby,omitempty"`
    	Profile `json:"profile,omitempty"`
    }
    // str:{"name":"七米","hobby":["足球","双色球"],"profile":{"site":"","slogan":""}}
    
    还需要使用嵌套的结构体指针:
    type User struct {
    	Name     string   `json:"name"`
    	Email    string   `json:"email,omitempty"`
    	Hobby    []string `json:"hobby,omitempty"`
    	*Profile `json:"profile,omitempty"`
    }
    // str:{"name":"七米","hobby":["足球","双色球"]}
    
优雅处理字符串格式的数字
  • 有时候,前端在传递来的json数据中可能会使用字符串类型的数字,这个时候可以在结构体tag中添加string来告诉json包从字符串中解析相应字段的数据:
    type Card struct {
    	ID    int64   `json:"id,string"`    // 添加string tag
    	Score float64 `json:"score,string"` // 添加string tag
    }
    
    func intAndStringDemo() {
    	jsonStr1 := `{"id": "1234567","score": "88.50"}`
    	var c1 Card
    	if err := json.Unmarshal([]byte(jsonStr1), &c1); err != nil {
    		fmt.Printf("json.Unmarsha jsonStr1 failed, err:%v\n", err)
    		return
    	}
    	fmt.Printf("c1:%#v\n", c1) // c1:main.Card{ID:1234567, Score:88.5}
    }
    
整数变浮点数
  • 在 JSON 协议中是没有整型和浮点型之分的,它们统称为number。json字符串中的数字经过Go语言中的json包反序列化之后都会成为float64类型。下面的代码便演示了这个问题:
    func jsonDemo() {
    	// map[string]interface{} -> json string
    	var m = make(map[string]interface{}, 1)
    	m["count"] = 1 // int
    	b, err := json.Marshal(m)
    	if err != nil {
    		fmt.Printf("marshal failed, err:%v\n", err)
    	}
    	fmt.Printf("str:%#v\n", string(b))
    	// json string -> map[string]interface{}
    	var m2 map[string]interface{}
    	err = json.Unmarshal(b, &m2)
    	if err != nil {
    		fmt.Printf("unmarshal failed, err:%v\n", err)
    		return
    	}
    	fmt.Printf("value:%v\n", m2["count"]) // 1
    	fmt.Printf("type:%T\n", m2["count"])  // float64
    }
    
  • 这种场景下如果想更合理的处理数字就需要使用decoder去反序列化,示例代码如下:
    func decoderDemo() {
    	// map[string]interface{} -> json string
    	var m = make(map[string]interface{}, 1)
    	m["count"] = 1 // int
    	b, err := json.Marshal(m)
    	if err != nil {
    		fmt.Printf("marshal failed, err:%v\n", err)
    	}
    	fmt.Printf("str:%#v\n", string(b))
    	// json string -> map[string]interface{}
    	var m2 map[string]interface{}
    	// 使用decoder方式反序列化,指定使用number类型
    	decoder := json.NewDecoder(bytes.NewReader(b))
    	decoder.UseNumber()
    	err = decoder.Decode(&m2)
    	if err != nil {
    		fmt.Printf("unmarshal failed, err:%v\n", err)
    		return
    	}
    	fmt.Printf("value:%v\n", m2["count"]) // 1
    	fmt.Printf("type:%T\n", m2["count"])  // json.Number
    	// 将m2["count"]转为json.Number之后调用Int64()方法获得int64类型的值
    	count, err := m2["count"].(json.Number).Int64()
    	if err != nil {
    		fmt.Printf("parse to int64 failed, err:%v\n", err)
    		return
    	}
    	fmt.Printf("type:%T\n", int(count)) // int
    }
    
处理不确定层级的json
  • 如果json串没有固定的格式导致不好定义与其相对应的结构体时,我们可以使用json.RawMessage原始字节数据保存下来。
    type sendMsg struct {
    	User string `json:"user"`
    	Msg  string `json:"msg"`
    }
    
    func rawMessageDemo() {
    	jsonStr := `{"sendMsg":{"user":"q1mi","msg":"永远不要高估自己"},"say":"Hello"}`
    	// 定义一个map,value类型为json.RawMessage,方便后续更灵活地处理
    	var data map[string]json.RawMessage
    	if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
    		fmt.Printf("json.Unmarshal jsonStr failed, err:%v\n", err)
    		return
    	}
    	var msg sendMsg
    	if err := json.Unmarshal(data["sendMsg"], &msg); err != nil {
    		fmt.Printf("json.Unmarshal failed, err:%v\n", err)
    		return
    	}
    	fmt.Printf("msg:%#v\n", msg)
    	// msg:main.sendMsg{User:"q1mi", Msg:"永远不要高估自己"}
    }
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值