Golang学习笔记(09-7-Json序列化)

645 阅读5分钟

1. 使用标准库

1.1. json序列化相关方法

1.	Marshal()
		Syntax: func Marshal(v interface{}) ([]byte, error) 
    Man:		将Go中数据类型转换为 json 字符串的字节切片
    
2.  Unmarshal()
		Syntax:	func Unmarshal(data []byte, v interface{}) error
    Man:		反序列化json字符串
    
3.  Indent()
		Syntax: func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
    Man:		缩进显示json字符串
    
4.	Compact()
		Syntax: func Compact(dst *bytes.Buffer, src []byte) error
    Man:		压缩无效空白符

1.2. json序列化

序列化中需要注意的点:

  • 因为是调用 json.Mashal() ,如果结构体字段使用小写将因为不可导出,而无法完成序列化。
  • json.Mashal()可以接收指针,因此实际操作中,都是传递指针而不是值
  • 如果要对结构体中字段名和json中不一致,需要在结构体定义时指定json中显示的名称

1.2.1. 序列化基本案例

  • Id和Name有明确指定 json 序列化后的字段名,因此按小写显示
  • Hobby中的describe字段小写开头,因此不可导出,在json序列化后的结果中不可见
package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Id    int			`json:"id"`
	Name  string		`json:"name"`
	Hobby []Hobby
}

type Hobby struct {
	Name     string
	describe string
}

func main() {
	var s0 = Student{
		Id: 10001,
		Name: "张三",
		Hobby: []Hobby{
			{
				Name:     "游泳",
				describe: "游泳。。。。",
			},
			{
				Name:     "钓鱼",
				describe: "台钓",
			},
		},
	}
	ret, err := json.Marshal(&s0)
	if err != nil {
		fmt.Println("序列化失败")
		return
	}
	fmt.Println(string(ret))
}
[root@heyingsheng models]# go run json/marshal.go
{"id":10001,"name":"张三","Hobby":[{"Name":"游泳"},{"Name":"钓鱼"}]}

1.2.2. 格式化json

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
)

func main()  {
	var str = `{"id":10001,"name":"张三","Hobby":[{"Name":"游泳"},{"Name":"钓鱼"}]}`
	// 格式化显示json字符串
	var s = bytes.Buffer{}
	err := json.Indent(&s, []byte(str), " ", "\t")
	if err != nil {
		fmt.Println("格式化失败,err=",err)
		return
	}
	ret := s.String()
	fmt.Println(ret)
	fmt.Println(strings.Repeat("##", 20))
	// 压缩无用的空白符号
	res := bytes.Buffer{}
	err = json.Compact(&res, []byte(ret))
	if err != nil {
		fmt.Println("格式化失败,err=",err)
		return
	}
	fmt.Println(res.String())
}
[root@heyingsheng models]# go run json/json.go
{
        "id": 10001,
        "name": "张三",
        "Hobby": [
                {
                        "Name": "游泳"
                },
                {
                        "Name": "钓鱼"
                }
        ]
 }
########################################
{"id":10001,"name":"张三","Hobby":[{"Name":"游泳"},{"Name":"钓鱼"}]}

1.3. json反序列化

1.3.1. 简单反序列化

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	Name  string		`json:"name"`
	Class []string		`json:"class"`
}

func main()  {
	str := `{"10001": {"name": "张三", "class": ["物理","化学","生物"]}}`
	var user01 map[string]User
	if err := json.Unmarshal([]byte(str), &user01); err != nil {
		fmt.Println("反序列化失败,err=", err)
		return
	}
	fmt.Printf("type:%T, value:%v\n", user01, user01)
	fmt.Println(user01["10001"].Class[0])
}
[root@heyingsheng models]# go run json/unmarshal.go
type:map[string]main.User, value:map[10001:{张三 [物理 化学 生物]}]
物理

1.3.2. 使用空接口接收数据

空接口可以接收任何结构的json字符串,但是非常难以转换为可操作的go语言数据类型,因为涉及到类型的断言!

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	Name  string		`json:"name"`
	Class []string		`json:"class"`
}

func main()  {
	str := `{"10001": {"name": "张三", "class": ["物理","化学","生物"]}}`
	var user01 interface{}
	if err := json.Unmarshal([]byte(str), &user01); err != nil {
		fmt.Println("反序列化失败,err=", err)
		return
	}
	fmt.Printf("type:%T, value:%v\n", user01, user01)
	// 使用类型断言才能取值
	if user, ok := user01.(map[string]interface {}); ok {
		fmt.Printf("type:%T, value:%v\n", user["10001"], user["10001"])
	}
}
[root@heyingsheng models]# go run json/unmarshal.go
type:map[string]interface {}, value:map[10001:map[class:[物理 化学 生物] name:张三]]
type:map[string]interface {}, value:map[class:[物理 化学 生物] name:张三]

1.4. json的tag

1.4.1. 忽略字段

type User struct {
	Name     string `json:"name"`
	UID      string `json:"uid"`
	Address  string `json:"-"`                  // 忽略该字段
	Password string `json:"password,omitempty"` // 忽略零值字段
	Phone    `json:"phone"`                     // 指定json后,将单层结构转为多层结构
	*Score   `json:"score,omitempty"`           // 如果字段为空则忽略,必须使用结构体指针,否则不行
}

type Phone struct {
	TelPhone string `json:"tel_phone,omitempty"`
	Mobile   string `json:"mobile,omitempty"`
}

type Score struct {
	MathScore  int `json:"math_score"`
	MusicScore int `json:"music_score"`
}

func main() {
	user01 := User{
		Name:     "ZhangSan",
		UID:      "0001",
		Address:  "南京",
		Password: "",
	}
	ret, _ := json.Marshal(user01)
	fmt.Println(string(ret))
}
[root@duduniao json]# go run main.go
{"name":"ZhangSan","uid":"0001","phone":{}}

1.4.2. 类型转换

在开发中,经常会出现服务调用接口,要求传递json中value为字符串,而代码结构体中为 bool 或者 int等其它类型。这种需求可以通过自定义的方法来实现转换,但是也有根据简单的方法,就是使用 tag 处理。

type Student struct {
	Name    string `json:"name"`
	Age     int8   `json:"age,string"`
	IsAdmin bool   `json:"is_admin,string"`
}

func main() {
	s1 := Student{
		"张三",
		int8(18),
		false,
	}
	ret, _ := json.Marshal(s1)
	fmt.Println(string(ret))
	fmt.Println(strings.Repeat("--", 20))
	str := `{"name":"李四","age":"39","is_admin":"true"}`
	_ = json.Unmarshal([]byte(str), &s1)
	fmt.Printf("%#v\n", s1)
}
[root@duduniao json]# go run main.go
{"name":"张三","age":"18","is_admin":"false"}
----------------------------------------
main.Student{Name:"李四", Age:39, IsAdmin:true}

2. 使用simpleJson处理json

在对象序列化为 json 字符串时,或者将json对象反序列化并赋值给一个结构体指针,这两种场景很适合使用标准库中得 json 包,但是如果只是从现有得 json 中某一些字段得值,那么可以考虑使用 simplejson 的链式调用完成处理。

2.1. simplejson常用方法

# "github.com/bitly/go-simplejson"
1.  Json 结构体
    type Json struct {
        // contains filtered or unexported fields
    }

2.  构造函数
    func NewJson(body []byte) (*Json, error)

3.  从 io.Reader 接口构造 Json对象
    func NewFromReader(r io.Reader) (*Json, error)

4.  断言相关的方法
    func (j *Json) String() (string, error)
    func (j *Json) Int() (int, error)
    func (j *Json) Int64() (int64, error)
    func (j *Json) Uint64() (uint64, error)
    func (j *Json) Bool() (bool, error)
    func (j *Json) Bytes() ([]byte, error)
    func (j *Json) StringArray() ([]string, error)
    func (j *Json) Interface() interface{}
    func (j *Json) Map() (map[string]interface{}, error)
    func (j *Json) Array() ([]interface{}, error)

5.  保证返回指定类型的结果, args 设定默认值
    func (j *Json) MustInt(args ...int) int
    func (j *Json) MustString(args ...string) string
    func (j *Json) MustFloat64(args ...float64) float64
    func (j *Json) MustBool(args ...bool) bool
    func (j *Json) MustStringArray(args ...[]string) []string
    func (j *Json) MustMap(args ...map[string]interface{}) map[string]interface{}
    func (j *Json) MustArray(args ...[]interface{}) []interface{}

6.  在map类型中,根据key取值
    func (j *Json) Get(key string) *Json

7.  更新json
    func (j *Json) Del(key string)
    func (j *Json) Set(key string, val interface{})

8.  序列化
    func (j *Json) Encode() ([]byte, error)
    func (j *Json) EncodePretty() ([]byte, error)   // 带缩进

2.2. 案例

** 测试的 Json 字符串如下 ** 

{
    "deploy_type":"task",
    "uniqid":"task-01",
    "labels":{
        "deploy_type":"db"
    },
    "callback_url":"https://xxx.xxx.xxx",
    "tasks":[
        {
            "cluster_type":"vm_cluster",
            "deploy_type":"vm_cluster",
            "action":"create",
            "labels":{
                "deploy_type":"cluster"
            },
            "uniqid":"cluster01",
            "name":"cluster01",
            "options":{
                "cadvisor":true
            }
        }
    ]
}

2.2.1. 取值

func main()  {
	sJson, err := simplejson.NewJson([]byte(str))
	if err != nil {
		fmt.Printf("New json failed, err:%s\n", err.Error())
		return
	}
	// 取labels
	res1 := sJson.Get("labels").Get("deploy_type").MustString("null")
	tmp1, _ := json.Marshal(sJson.Get("tasks").MustArray()[0])  // 返回值为 interface,需要重新序列化
	tmp2, _ := simplejson.NewJson(tmp1)
	res2 := tmp2.Get("labels").Get("deploy_type").MustString()
	fmt.Printf("res1:%s,res2:%s\n", res1, res2)
}

2.2.2 修改值

func main()  {
	sJson, err := simplejson.NewJson([]byte(str))
	if err != nil {
		fmt.Printf("New json failed, err:%s\n", err.Error())
		return
	}
	// 删除值
	sJson.Del("tasks")  // 删除task字段
	res1, _:= sJson.EncodePretty()
	fmt.Println(string(res1))
	// 修改值
	sJson.Set("labels", false)  // 修改labels字段
	res2, _:= sJson.EncodePretty()
	fmt.Println(string(res2))
}