Tips for using JSON in Go

Basic serialization:

First, let's take a look at the basic usage of json.Marshal()(serialization) and json.Unmarshal(deserialization) in Go language .

type Person struct {
	Name   string
	Age    int64
	Weight float64
}

func main() {
	p1 := Person{
		Name:   "七米",
		Age:    18,
		Weight: 71.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)
}

  Output:

str:{"Name":"七米","Age":18,"Weight":71.5}
p2:main.Person{Name:"七米", Age:18, Weight:71.5}

Structure tag introduction:

TagIt is the meta information of the structure, which can be read through the reflection mechanism at runtime. TagIt is defined behind the structure field and is wrapped by a pair of backticks . The specific format is as follows:

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

The structure tag consists of one or more key-value pairs. Keys and values ​​are separated by colons , and values ​​are enclosed in double quotes . Multiple key-value pair tags can be set in the same structure field, and different key-value pairs are separated by spaces .

Use json tag to specify the field name:

Serialization and deserialization use the field names of the structure by default. We can specify the field names generated by json serialization by adding tags to the structure fields:

// Use json tag to specify the behavior when serializing and deserializing. 
Type Person struct { 
	Name string `json:" name "` // Specify the lowercase name when using json serialization / deserialization. 
	Age int64 
	Weight float64 
}

Ignore a field:

If you want to ignore a field in the structure during json serialization / deserialization, you can add it to the tag as follows  -.

// Use json tag to specify json serialization and deserialization behavior 
Type Person struct { 
	Name string `json:" name "` // Specify json serialization / deserialization using lowercase name 
	Age int64 
	Weight float64 `json: "-" `// Ignore this field when specifying json serialization / deserialization 
}

Ignore empty fields:

When the fields in the struct have no value, json.Marshal()these fields will not be ignored during serialization, but the default output field's type zero value (for example int, the floattype zero value is 0, the stringtype zero value is "", and the object type zero value is nil). If you want to ignore these fields with no value during serialization, you can add omitemptytags to the corresponding fields .

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)
}

  Output result:

str:{"name":"七米","email":"","hobby":null}

  If you want to remove the null value field in the final serialization result, you can define the structure as follows:

// Add omitempty to the tag and ignore the null value 
// Note that hobby and omitempty together are json tag values, separated by commas in the middle 
type User struct { 
	Name string `json:" name "` 
	Email string `json:" email, omitempty "` 
	Hobby [] string `json:" hobby, omitempty "` 
}

  At this time, execute the above again omitemptyDemo, the output is as follows:

str: {"name": "七 米"} // There are no email and hobby fields in the serialized result

 Ignore empty value fields of nested structures:

First look at examples of nesting several structures:

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)
}

 The Profileserialized json string is a single layer when anonymous nesting :

str:{"name":"七米","hobby":["足球","双色球"],"site":"","slogan":""}

  If you want to turn into a nested json string, you need to change to named nesting or define a field 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":""}}

  If you want to ignore this field when the nested structure is empty, adding omitempty is not enough:

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":""}}

  You also need to use nested structure pointers:

type User struct {
	Name     string   `json:"name"`
	Email    string   `json:"email,omitempty"`
	Hobby    []string `json:"hobby,omitempty"`
	*Profile `json:"profile,omitempty"`
}
// str:{"name":"七米","hobby":["足球","双色球"]}

  

Do not modify the original structure and ignore the null value field:

We need json serialization User, but we do n’t want to serialize the password, and do n’t want to modify the Userstructure. At this time, we can use to create another structure PublicUseranonymous nesting original User, and specify the Passwordfield as an anonymous structure pointer type, and add omitemptytag, The sample code is as follows:

type User struct {
	Name     string `json:"name"`
	Password string `json:"password"`
}

type PublicUser struct {
	*User             // 匿名嵌套
	Password *struct{} `json:"password,omitempty"`
}

func omitPasswordDemo() {
	u1 := User{
		Name:     "七米",
		Password: "123456",
	}
	b, err := json.Marshal(PublicUser{User: &u1})
	if err != nil {
		fmt.Printf("json.Marshal u1 failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)  // str:{"name":"七米"}

  

Elegantly handle numbers in string format:

Sometimes, the front end may use string type numbers in the json data passed in. At this time, it can be added in the structure tag stringto tell the json package to parse the data of the corresponding field from the string:

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}
}

  

Integer to floating point:

In the JSON protocol, there is no distinction between integer and floating point types, and they are collectively called number. The numbers in the json string will become the float64type after deserialization of the json package in Go language . The following code demonstrates this problem:

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
}

  In this scenario, if you want to handle numbers more reasonably, you need to use decoderdeserialization. The sample code is as follows

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 
	// After converting m2 ["count"] to json.Number, call the Int64 () method to obtain a value of type 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

  The source code of json.Number is defined as follows:

// A Number represents a JSON number literal.
type Number string

// String returns the literal text of the number.
func (n Number) String() string { return string(n) }

// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
	return strconv.ParseFloat(string(n), 64)
}

// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
	return strconv.ParseInt(string(n), 10, 64)
}

 We need to get the json.Number type when processing the number type json field, and then call Float64 () or Int64 () according to the actual type of the field.

Custom resolution time field:

The built-in json package of Go language uses the time format defined in the RFC3339 standard, which has many restrictions when we serialize the time field.

type Post struct {
	CreateTime time.Time `json:"create_time"`
}

func timeFieldDemo() {
	p1 := Post{CreateTime: time.Now()}
	b, err := json.Marshal(p1)
	if err != nil {
		fmt.Printf("json.Marshal p1 failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)
	jsonStr := `{"create_time":"2020-04-05 12:25:42"}`
	var p2 Post
	if err := json.Unmarshal([]byte(jsonStr), &p2); err != nil {
		fmt.Printf("json.Unmarshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("p2:%#v\n", p2)
}

  The output of the above code is as follows:

str:{"create_time":"2020-04-05T12:28:06.799214+08:00"}
json.Unmarshal failed, err:parsing time ""2020-04-05 12:25:42"" as ""2006-01-02T15:04:05Z07:00"": cannot parse " 12:25:42"" as "T"

That is, the built-in json package does not recognize our commonly used string time format, such as 2020-04-05 12:25:42.

However, we implement custom event format parsing by implementing the json.Marshaler / json.Unmarshaler interface.

type CustomTime struct {
	time.Time
}

const ctLayout = "2006-01-02 15:04:05"

var nilTime = (time.Time{}).UnixNano()

func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	if s == "null" {
		ct.Time = time.Time{}
		return
	}
	ct.Time, err = time.Parse(ctLayout, s)
	return
}

func (ct *CustomTime) MarshalJSON() ([]byte, error) {
	if ct.Time.UnixNano() == nilTime {
		return []byte("null"), nil
	}
	return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ctLayout))), nil
}

func (ct *CustomTime) IsSet() bool {
	return ct.UnixNano() != nilTime 
}

type Post struct {
	CreateTime CustomTime `json:"create_time"`
}

func timeFieldDemo() {
	p1 := Post{CreateTime: CustomTime{time.Now()}}
	b, err := json.Marshal(p1)
	if err != nil {
		fmt.Printf("json.Marshal p1 failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)
	jsonStr := `{"create_time":"2020-04-05 12:25:42"}`
	var p2 Post
	if err := json.Unmarshal([]byte(jsonStr), &p2); err != nil {
		fmt.Printf("json.Unmarshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("p2:%#v\n", p2)
}

  

Custom MarshalJSON and UnmarshalJSON methods:

The custom type method above is a bit more verbose. Let's look at a relatively convenient method.

The first thing you need to know is that if you can implement MarshalJSON()([]byte, error)and UnmarshalJSON(b []byte) errormethods for a type , then this type will use the corresponding method you customized when serializing (MarshalJSON) / deserializing (UnmarshalJSON).

type Order struct { 
	ID int `json:" id "` 
	Title string `json:" title "` 
	CreatedTime time.Time `json:" created_time "` 
} 

const layout = "2006-01-02 15:04:05" 

/ / MarshalJSON implements a custom MarshalJSON method for Order type 
func (o * Order) MarshalJSON () ([] byte, error) { 
	type TempOrder Order // define a new type consistent with the Order field 
	return json.Marshal (struct { 
		CreatedTime string `json:" created_time "` 
		* TempOrder // Avoid nesting Order directly into an endless loop 
	} { 
		CreatedTime: o.CreatedTime.Format (layout), 
		TempOrder: (* TempOrder) (o),
	}) 
} 

// UnmarshalJSON implements a custom UnmarshalJSON method for Order type  
func func (o * Order) UnmarshalJSON (data [] byte) error {
	type TempOrder Order // defines a new type consistent with the Order field 
	ot: = struct { 
		CreatedTime string `json:" created_time "` 
		* TempOrder // Avoid directly nesting Order into an infinite loop 
	} { 
		TempOrder: (* TempOrder) (o) , 
	} 
	if err: = json.Unmarshal (data, & ot); err! = nil { 
		return err 
	} 
	var err error 
	o.CreatedTime, err = time.Parse (layout, ot.CreatedTime) 
	if err! = nil { 
		return err 
	} 
	return nil 
} 

// Custom serialization method 
func customMethodDemo () { 
	o1: = Order { 
		ID: 123456, 
		Title: "" Go Study Notes of Seven Meters "", 
		CreatedTime: time.Now (), 
	} 
	// implement struct-> json string 
	b, err through custom MarshalJSON method : = json.Marshal (& o1)
	if err! = nil { 
		fmt.Printf ("json.Marshal o1 failed, err:% v \ n", err) 
		return 
	} 
	fmt.Printf ("str:% s \ n", b) 
	// Through custom The UnmarshalJSON method implements json 
	string- > struct jsonStr: = `{" created_time ":" 2020-04-05 10:18:20 "," id ": 123456," title ":" "Go Learning Notes for Seven Meters" " } ` 
	var o2 Order 
	if err: = json.Unmarshal ([] byte (jsonStr), & o2); err! = nil { 
		fmt.Printf (" json.Unmarshal failed, err:% v \ n ", err) 
		return 
	} 
	fmt.Printf ("o2:% # v \ n", o2) 
}


  Output result:

str: {"created_time": "2020-04-05 10:32:20", "id": 123456, "title": "" Go Learning Notes of Seven Meters ""} 
o2: main.Order {ID: 123456 , Title: "" Go Learning Notes for Seven Meters "", CreatedTime: time.Time {wall: 0x0, ext: 63721678700, loc: (* time.Location) (nil)}}

Add field using anonymous structure

Using the embedded structure can expand the fields of the structure, but sometimes we don't need to define a new structure separately, you can use anonymous structures to simplify operations:
type UserInfo struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func anonymousStructDemo() {
	u1 := UserInfo{
		ID:   123456,
		Name: "七米",
	}
	// 使用匿名结构体内嵌User并添加额外字段Token
	b, err := json.Marshal(struct {
		*UserInfo
		Token string `json:"token"`
	}{
		&u1,
		"91je3a4s72d1da96h",
	})
	if err != nil {
		fmt.Printf("json.Marsha failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)
	// str:{"id":123456,"name":"七米","token":"91je3a4s72d1da96h"}
}

  

Use anonymous structures to combine multiple structures

Similarly, you can also use anonymous structures to combine multiple structures to serialize and deserialize data:

type Comment struct {
	Content string
}

type Image struct {
	Title string `json:"title"`
	URL   string `json:"url"`
}

func anonymousStructDemo2() {
	c1 := Comment{
		Content: "永远不要高估自己",
	}
	i1 := Image{
		Title: "赞赏码",
		URL:   "https://www.liwenzhou.com/images/zanshang_qr.jpg",
	}
	// struct -> json string
	b, err := json.Marshal(struct {
		*Comment
		*Image
	}{&c1, &i1})
	if err != nil {
		fmt.Printf("json.Marshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)
	// json string -> struct
	jsonStr := `{"Content":"永远不要高估自己","title":"赞赏码","url":"https://www.liwenzhou.com/images/zanshang_qr.jpg"}`
	var (
		c2 Comment
		i2 Image
	)
	if err := json.Unmarshal([]byte(jsonStr), &struct {
		*Comment
		*Image
	}{&c2, &i2}); err != nil {
		fmt.Printf("json.Unmarshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("c2:%#v i2:%#v\n", c2, i2)
}

  Output:

str: {"Content": "Never overestimate yourself", "title": "Appreciation Code", "url": "https://www.liwenzhou.com/images/zanshang_qr.jpg"} 
c2: main. Comment {Content: "Never overestimate yourself"} i2: main.Image {Title: "Appreciation Code", URL: "https://www.liwenzhou.com/images/zanshang_qr.jpg"}

  

Handling json at an uncertain level

If the json string has no fixed format and it is difficult to define the corresponding structure, we can use the json.RawMessageoriginal byte data to save it.

type sendMsg struct { 
	User string `json:" user "` 
	Msg string `json:" msg "` 
} 

func rawMessageDemo () { 
	jsonStr: = `{" sendMsg ": {" user ":" q1mi "," msg ": "Never overestimate yourself"}, "say": "Hello"} ` 
	// Define a map with a value type of json.RawMessage to facilitate more flexible subsequent processing of 
	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: "Never overestimate yourself"} 
}

  

 

Guess you like

Origin www.cnblogs.com/FSH1014/p/12709922.html