5

Golang系列教程--JSON解析

 2 years ago
source link: https://jjmeg.github.io/posts/golang-json/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Golang系列教程--JSON解析

2020.5.6 1730 4 分钟

json

提供了 JSON 与 Go 变量之间的序列化与反序列化。

📜本文内容


JSON序列化

func Marshal(v interface{}) ([]byte, error)

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	Name    string `json:"name"`
	Country string `json:"country"`
	City    string `json:"city"`
}

func main() {
	amy := person{
		Name:    "Amy",
		Country: "China",
		City:    "Beijing",
	}
	data1, _ := json.Marshal(amy)
	fmt.Println(string(data1))
	data2, _ := json.MarshalIndent(amy, "", "    ")
	fmt.Println(string(data2))
}

使用 MarshalIndent() 可使数据输出的形式美观一些

{"name":"Amy","country":"China","city":"Beijing"}
{
   "name": "Amy",
   "country": "China",
   "city": "Beijing"
}

tag及使用技巧

1. 省略空字段 omitempty

package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	Name    string `json:"name"`
	Country string `json:"country"`
	City    string `json:"city,omitempty"`
}

func main() {
	amy := person{
		Name:    "Amy",
	}
	data, _ := json.MarshalIndent(amy, "", "    ")
	fmt.Println(string(data))
}

JSON内的 city 字段增加 omitempty 关键字,初始化对象时未对该字段赋值,在序列化后将不显示;country 字段未注明忽略空值,序列化赋了默认0值:

{
    "name": "Amy",
    "country": ""
}
2. 忽略特定字段 -
package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	Name    string `json:"name"`
	Country string `json:"country"`
	City    string `json:"-"`
}

func main() {
	amy := person{
		Name:    "Amy",
		Country: "China",
		City:    "Beijing",
	}
	data, _ := json.MarshalIndent(amy, "", "    ")
	fmt.Println(string(data))
}

即使字段已经初始化,序列化后的内容也不会包含此字段

{
    "name": "Amy",
    "country": "China"
}
3. JSON字段类型转换
package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	Name    string `json:"name"`
	Age     int `json:"age,string"`
}

func main() {
	amy := person{
		Name:    "Amy",
		Age:     25,
	}

	data, _ := json.MarshalIndent(amy,"","    ")
	fmt.Printf("%s\n\n", string(data))
}

结构体内的 Age 字段为整型,加上 string 将本字段的值转化为字符串类型:

{
   "name": "Amy",
   "age": "25"
}
4. 临时添加新字段
package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	Name    string `json:"name"`
	Country string `json:"country"`
	City    string `json:"city"`
}

func main() {
	amy := person{
		Name:    "Amy",
		Country: "China",
		City:    "Beijing",
	}
	data, _ := json.MarshalIndent(
		struct {
			person
			Gender string `json:"gender"`
		}{
			person: amy,
			Gender: "female",
		},"","    ")
	fmt.Println(string(data))
}

在序列化时新建一个结构体即可:

{
    "name": "Amy",
    "country": "China",
    "city": "Beijing",
    "gender": "female"
}

5. 多结构体组合成

从4中得到启发,可将多个结构体的字段组合成一个JSON

package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	Name    string `json:"name"`
	Country string `json:"country"`
	City    string `json:"city"`
}

type occupation struct {
	Company  string `json:"company"`
	Position string `json:"position"`
}

func main() {
	amy := person{
		Name:    "Amy",
		Country: "China",
		City:    "Beijing",
	}

	job := occupation{
		Company:  "Google",
		Position: "developer",
	}
	data, _ := json.MarshalIndent(
		struct {
			person
			occupation
		}{
			person:     amy,
			occupation: job,
		}, "", "    ")
	fmt.Println(string(data))
}
{
    "name": "Amy",
    "country": "China",
    "city": "Beijing",
    "company": "Google",
    "position": "developer"
}

6. 对时间类型的序列化

Go对时间的序列化方式是格式化转化成字符串,并默认使用RFC3339标准 2006-01-02T15:04:05.999999999Z07:00 做格式化,时间对象转化为JSON默认使用 MarshalJSON() 方法

func (t Time) MarshalJSON() ([]byte, error) {
	if y := t.Year(); y < 0 || y >= 10000 {
		// RFC 3339 is clear that years are 4 digits exactly.
		// See golang.org/issue/4556#c15 for more discussion.
		return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
	}

	b := make([]byte, 0, len(RFC3339Nano)+2)
	b = append(b, '"')
	b = t.AppendFormat(b, RFC3339Nano)
	b = append(b, '"')
	return b, nil
}

Marshaler 接口定义了 MarshalJSON() 方法,Go中的时间 time.Time 实现了 Marshaler 接口:

type Marshaler interface {
        MarshalJSON() ([]byte, error)
}

结合上述思路,我们可以自定义时间类型,实现 Marshaler 接口,达到自定义序列化的目的:

package main

import (
	"encoding/json"
	"fmt"
	"time"
)

//自定义时间类型
type MyTime time.Time

// 实现Marshaler接口
func (mytime MyTime) MarshalJSON() ([]byte, error) {
	n := time.Time(mytime).Format("2006-01-02 15:04")
	t := "\"" + n + "\""
	return []byte(t), nil
}

type person struct {
	Name     string `json:"name"`
	Country  string `json:"country"`
	City     string `json:"city"`
	Birthday MyTime `json:"birthday"`
}

func main() {
	amy := person{
		Name:     "Amy",
		Country:  "China",
		City:     "Beijing",
		Birthday: MyTime(time.Now()),
	}
	data, _ := json.MarshalIndent(amy, "", "    ")
	fmt.Println(string(data))
}

本例将时间输出格式修改为 年-月-日 时:分

{
    "name": "Amy",
    "country": "China",
    "city": "Beijing",
    "birthday": "2019-09-05 18:42"
}

JSON反序列化

func Unmarshal(data []byte, v interface{}) error

package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	Name    string `json:"name"`
	Country string `json:"country"`
	City    string `json:"city"`
}

func main() {
	data := `{
	"name": "Amy",
	"country": "China",
	"city":"Beijing"
	}`
	amy := person{}
	err := json.Unmarshal([]byte(data), &amy)
	if err == nil {
		fmt.Printf("%s\n%s\n%s\n", amy.Name, amy.Country, amy.City)
	}
}

反序列化完成字符串到对象的转化,当 Unmarshal() 方法返回错误不空表示反序列化失败

Amy
China
Beijing

JSON反序列化的常用技巧

拆分成多个结构体

package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	Name    string `json:"name"`
	Country string `json:"country"`
	City    string `json:"city"`
}

type occupation struct {
	Company  string `json:"company"`
	Position string `json:"position"`
}

func main() {
	data := `{
	"name": "Amy",
	"country": "China",
	"city":"Beijing",
	"company":"Google",
	"position":"developer"
	}`

	amy := person{}
	job := occupation{}

	err := json.Unmarshal([]byte(data),
		&struct {
			*person
			*occupation
		}{&amy, &job})
	if err == nil {
		fmt.Printf("%v\n%v\n", amy, job)
	}
}
{Amy China Beijing}
{Google developer}

JSON与私有属性

package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name    string `json:"name"`
	country string `json:"country"`
}

func main() {
	d := `{
	"name": "Amy",
	"country": "China"
	}`

	amy := Person{}

	err := json.Unmarshal([]byte(d),&amy)
	if err == nil {
		fmt.Printf("%v\n", amy)
	}

  Sam := Person{
    Name: "Sam",
    country: "CN",
  }
  data,err := json.Marshal(Sam)
	if err == nil {
		fmt.Printf("%v\n", string(data))
	}
}

Person 内的 country 是私有属性,JSON的序列化及反序列化都不会对私有属性转化。

{Amy}
{"name":"Sam"}
👏欢迎评论👏

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK