Golang中JSON

273 阅读8分钟

解析Json的两种方案

解析为Maps

元素使用前需要进行类型判断,number默认会转为float64,所以类型判断的时候必须用.(float64)判断

Go TypeJSON Type
boolboolean
float64number
stringstring
nil pointernull
time.TimeRFC 3339 timestamp (string)
package main 
import ( 
    "encoding/json" 
    "fmt" 
) 
func main() { 
    // Create a map to parse the JSON 
    var data map[string]interface{} 
    // Define a JSON string 
    j := `{"name":"example","numbers":[1,2,3,4],"nested":{"isit":true,"description":"a nested json"}}` 
    // Parse our JSON string 
    err := json.Unmarshal([]byte(j), &data) 
    if err != nil { 
        fmt.Printf("Error parsing JSON string - %s", err) 
    } 
    // Print out one of our JSON values 
    fmt.Printf("Name is %s, Numbers[0] is %f", data["name"].(string), data["numbers"].([]interface{})[0].(float64))
}

输出:

map[name:example nested:map[description:a nested json isit:true] numbers:[1 2 3 4]]
Name is example, Numbers[0] is 1.000000
Program exited.

解析为Struct

package main

import (
	"encoding/json"
	"fmt"
)

// Example is our main data structure used for JSON parsing
type Example struct {
	Name    string `json:"name"`
	Numbers []int  `json:"numbers"`
	Nested  Nested `json:"nested"`
}

// Nested is an embedded structure within Example
type Nested struct {
	IsIt        bool   `json:"isit"`
	Description string `json:"description"`
}

func main() {
	// Define a JSON string
	j := `{"name":"example","numbers":[1,2,3,4],"nested":{"isit":true,"description":"a nested json"}}`

	// Parse JSON string into data object
	var data Example
	err := json.Unmarshal([]byte(j), &data)
	if err != nil {
		fmt.Printf("Error parsing JSON string - %s", err)
	}
	// Print the name
	fmt.Printf("Name is %s", data.Name)
}

匿名结构体

package main

import (
    "fmt"
    "encoding/json"
)

type Config struct {
    Server struct {
        Host string `json:"host"`
        Port string `json:"port"`
        } `json:"server"`
    Postgres struct {
        Host     string `json:"host"`
        User     string `json:"user"`
        Password string `json:"password"`
        DB       string `json:"db"`
    } `json:"database"`
}

func main() {
    jsonConfig := []byte(`{
        "server":{
            "host":"localhost",
            "port":"8080"},
        "database":{
            "host":"localhost",
            "user":"db_user",
            "password":"supersecret",
            "db":"my_db"}}`)
    var config Config
    err := json.Unmarshal(jsonConfig, &config)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Config: %+v\n", config)
}

输出:

Config: {Server:{Host:localhost Port:8080} Postgres:{Host:localhost User:db_user Password:supersecret DB:my_db}}

Program exited.

Unexported Filed

struct to json

Struct转成json的时候,会自动忽略unexported(首字母小写)的字段,比如下图中的weight,如果想被解析为json,则需要将weight的首字母大写。

package main

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

type PersonAllowEmpty struct {
	Name     string
	Age      int64
	Birth    time.Time
	weight string //The encoding/json package and similar packages ignore unexported fields.
}

func main() {
	person := PersonAllowEmpty{}
	jsonBytes, _ := json.Marshal(person)
	fmt.Println(string(jsonBytes))
}

输出:

{"Name":"","Age":0,"Birth":"0001-01-01T00:00:00Z"}

Program exited.

空字段问题

struct to json

package main

import (
	"encoding/json"
	"fmt"
        "time"
)
type Person struct {
    Name     string
    Age      int64
    Birth    time.Time
    Children []Person
}

func main() {
    person := Person{}

    jsonBytes, _ := json.Marshal(person)
    fmt.Println(string(jsonBytes)) 
}

输出:

{"Name":"","Age":0,"Birth":"0001-01-01T00:00:00Z","Children":null}

Program exited.

当 struct 中的字段没有值时,使用 json.Marshal 方法并不会自动忽略这些字段,而是根据字段的类型输出了他们的默认空值,这往往和我们的预期不一致,json 包提供了对字段的控制手段,我们可以为字段增加 omitempty tag,这个 tag 会在字段值为零值(int 和 float 类型零值是 0,string 类型零值是 "",对象类型零值是 nil)时,忽略该字段。

struct to json带上标签

package main

import (
	"encoding/json"
	"fmt"
        "time"
)
type PersonAllowEmpty struct {
    Name     string             `json:",omitempty"`
    Age      int64              `json:",omitempty"`
    Birth    time.Time          `json:",omitempty"`
    Children []PersonAllowEmpty `json:",omitempty"`
    unexport string
}

func main() {
    person := PersonAllowEmpty{}
    jsonBytes, _ := json.Marshal(person)
    fmt.Println(string(jsonBytes))  
}

输出:

{"Birth":"0001-01-01T00:00:00Z"}

Program exited.

可以看到,这次输出的 json 中只有 Birth 字段了,string、int、对象类型的字段,都因为没有赋值,默认是零值,所以被忽略,对于日期时间类型,由于不可以设置为零值,也就是 0000-00-00 00:00:00,不会被忽略。

struct to json永久忽略字段

如果想要某一个字段在任何情况下都被 json 包忽略,需要使用如下的写法。

package main

import (
	"encoding/json"
	"fmt"
        "time"
)
type Person struct {
    Name     string `json:"-"`
    Age      int64 `json:"-"`
    Birth    time.Time `json:"-"`
    Children []string `json:"-"`
}

func main() {
    birth, _ := time.Parse(time.RFC3339, "1988-12-02T15:04:27+08:00")
    person := Person{
        Name: "Wang Wu",
        Age: 30,
        Birth: birth,
        Children: []string{},
    }

    jsonBytes, _ := json.Marshal(person)
    fmt.Println(string(jsonBytes))  
}

输出:

{}

Program exited.

json to struct 字段不全时会使用默认值

package main

import (
	"encoding/json"
	"fmt"
        "time"
)
type Person struct {
    Name     string
    Age      int64
    Birth    time.Time
    Children []Person
}

func main() {
    var person Person
    data := []byte(`{"Name": "1"}`)
    err := json. Unmarshal(data, &person)
    fmt.Println(person, err) 
}

输出

{1 0 0001-01-01 00:00:00 +0000 UTC []} <nil>

Program exited.

json to struct 字段值为null也会使用默认值

package main

import (
	"encoding/json"
	"fmt"
        "time"
)
type Person struct {
    Name     string
    Age      int64
    Birth    time.Time
    Children []Person
}

func main() {
    var person Person
    data := []byte(`{"Name": null}`)
    err := json. Unmarshal(data, &person)
    fmt.Println(person, err) 
}

输出:

{ 0 0001-01-01 00:00:00 +0000 UTC []} <nil>

Program exited.

字段不匹配

json中字段多

package main

import "fmt"
import "encoding/json"

type AutoGenerated struct {
	Aa int `json:"aa"`
	B  int `json:"b"`
}
func main() {
	c := "{\"aa\": 1, \"b\":2, \"c\":3}"
	var a AutoGenerated
	json.Unmarshal([]byte(c), &a)
	fmt.Println(a)
}

输出:

{1 2}

Program exited.

解析嵌套interface

有时候返回的结果可能有多种,这个时候就可能把结构体的字段定义为interface,如

// YiqiuResponse  defined yiqiu basic response
type YiqiuResponse struct {
   Success bool        `json:"success"`
   Message string      `json:"message"`
   Code    int         `json:"code"`
   Data    interface{} `json:"data"`
}

如何继续把interface解析为struct

第一种

//https://stackoverflow.com/questions/43325288/golang-convert-interface-to-struct/43325450
type Customer struct {
    Name string `json:"name"`
}

type UniversalDTO struct {
    Data interface{} `json:"data"`
    // more fields with important meta-data about the message...
}

func main() {
    // create a customer, add it to DTO object and marshal it
    customer := Customer{Name: "Ben"}
    dtoToSend := UniversalDTO{customer}
    byteData, _ := json.Marshal(dtoToSend)

    // unmarshal it (usually after receiving bytes from somewhere)
    receivedDTO := UniversalDTO{}
    json.Unmarshal(byteData, &receivedDTO)

    //Attempt to unmarshall our customer
    receivedCustomer := getCustomerFromDTO(receivedDTO.Data)
    fmt.Println(receivedCustomer)
}

func getCustomerFromDTO(data interface{}) Customer {
    customer := Customer{}
    bodyBytes, _ := json.Marshal(data)
    json.Unmarshal(bodyBytes, &customer)
    return customer
}

第二种,better

type Customer struct {
    Name string `json:"name"`
}

type UniversalDTO struct {
    Data interface{} `json:"data"`
    // more fields with important meta-data about the message...
}

func main() {
    // create a customer, add it to DTO object and marshal it
    customer := Customer{Name: "Ben"}
    dtoToSend := UniversalDTO{customer}
    byteData, _ := json.Marshal(dtoToSend)

    // unmarshal it (usually after receiving bytes from somewhere)
    receivedCustomer := &Customer{}
    receivedDTO := UniversalDTO{Data: receivedCustomer}
    json.Unmarshal(byteData, &receivedDTO)

    //done
    fmt.Println(receivedCustomer)
}

结构体方法自我解析函数

https://zhuanlan.zhihu.com/p/116638548

null in json

The following case will be encoded as null:

  • Array and slice values encode as JSON arrays, except that []byte encodes as a base64-encoded string, and a nil slice encodes as the null JSON value.
  • Pointer values encode as the value pointed to. A nil pointer encodes as the null JSON value.
  • Interface values encode as the value contained in the interface. A nil interface value encodes as the null JSON value.
  • The JSON null value unmarshals into an interface, map, pointer, or slice by setting that Go value to nil. Because null is often used in JSON to mean “not present,” unmarshaling a JSON null into any other Go type has no effect on the value and produces no error.

array and slice

Marshal

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	var x []int
	xJSON, err := json.Marshal(x)
	if err != nil {
		fmt.Println("failed to marshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(string(xJSON))

	x = make([]int, 0, 0)
	xxJSON, err := json.Marshal(x)
	if err != nil {
		fmt.Println("failed to marshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(string(xxJSON))
}

输出:

true
null
false
[]

Unmarshal

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	bs := []byte(`null`)
	var x []int
	fmt.Println(x == nil)
	if err := json.Unmarshal(bs, &x); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(x)

	bs = []byte(`[123,456]`)
	var xx []int
	fmt.Println(xx == nil)
	if err := json.Unmarshal(bs, &xx); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println(xx == nil)
	fmt.Println(xx)
}

输出:

true
true
[]
true
false
[123 456]

pointer

Marshal

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	var x *int
	xJSON, err := json.Marshal(x)
	if err != nil {
		fmt.Println("failed to marshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(string(xJSON))

	num := 110
	x = &num
	xxJSON, err := json.Marshal(x)
	if err != nil {
		fmt.Println("failed to marshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(string(xxJSON))
}

输出:

true
null
false
110

Unmarshal

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	bs := []byte(`null`)
	var x *int
	fmt.Println(x == nil)
	if err := json.Unmarshal(bs, &x); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(x)

	bs = []byte(`123`)
	var xx *int
	fmt.Println(xx == nil)
	if err := json.Unmarshal(bs, &xx); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println(xx == nil)
	fmt.Println(xx, *xx)
}

输出:

true
true
<nil>
true
false
0xc0000140a8 123

interface

Marshal

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	var x interface{}
	xJSON, err := json.Marshal(x)
	if err != nil {
		fmt.Println("failed to marshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(string(xJSON))

	x = 110
	xxJSON, err := json.Marshal(x)
	if err != nil {
		fmt.Println("failed to marshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(string(xxJSON))
}

输出:

true
null
false
119

Unmarshal

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	bs := []byte(`null`)
	var x interface{}
	fmt.Println(x == nil)
	if err := json.Unmarshal(bs, &x); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(x)

	bs = []byte(`123`)
	var xx interface{}
	fmt.Println(xx == nil)
	if err := json.Unmarshal(bs, &xx); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println(xx == nil)
	fmt.Println(xx)
}

输出:

true
true
<nil>
true
false
123

map

Marshal

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	var x map[string]string
	xJSON, err := json.Marshal(x)
	if err != nil {
		fmt.Println("failed to marshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(string(xJSON))

	x = make(map[string]string)
	xxJSON, err := json.Marshal(x)
	if err != nil {
		fmt.Println("failed to marshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(string(xxJSON))
}

输出:

true
null
false
{}

Unmarshal

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	bs := []byte(`null`)
	var x map[string]string
	fmt.Println(x == nil)
	if err := json.Unmarshal(bs, &x); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println(x == nil)
	fmt.Println(x)

	bs = []byte(`{"abc":"123"}`)
	var xx map[string]string
	fmt.Println(xx == nil)
	if err := json.Unmarshal(bs, &xx); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println(xx == nil)
	fmt.Println(xx)
}

输出:

true
true
map[]
true
false
map[abc:123]

结构体

  • null 是个特殊字符串,经过 json.Unmarshal 处理后,返回的 err 为 nil
  • p1 定义为 person 本身,null 解析后,person 所有成员字段均为 golang 默认值
  • p2 定义为 person 指针,null 解析后,p2 为 nil 指针
package main

import (
	"encoding/json"
	"fmt"
)

type person struct {
	name    string
	age     int
	friends []string
	phone   *string
}

func main() {
	pJSON := []byte("null")

	var p1 person
	if err := json.Unmarshal(pJSON, &p1); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println("p1", p1, p1.phone == nil, p1.friends == nil)

	var p2 *person = nil
	if err := json.Unmarshal(pJSON, &p2); err != nil {
		fmt.Println("failed to unmarshal, err:", err.Error())
		return
	}
	fmt.Println("p2", p2 == nil, p2)
}

输出:

p1 { 0 [] <nil>} true true
p2 true <nil>

结构体指针

结构体以及结构体指针转成json效果是一样的。

struct to json

package main

import (
	"encoding/json"
	"fmt"
)

type test struct {
	A string
	B int
}

func main() {
	ts1 := make([]test, 2)
	tps1 := make([]*test, 2)
	for idx := 0; idx < 2; idx++ {
		ts1[idx].A = "a"
		ts1[idx].B = 1
		tps1[idx] = &test{
			A: "a",
			B: 1,
		}
	}
	ts1byte, _ := json.Marshal(ts1[0])
	tps1byte, _ := json.Marshal(tps1[0])
	fmt.Println(string(ts1byte))
	fmt.Println(string(tps1byte))
}

输出:

{"A":"a","B":1}
{"A":"a","B":1}

结构体数组和结构体指针数组也一样

package main

import (
	"encoding/json"
	"fmt"
)

type test struct {
	A string
	B int
}

func main() {
	ts1 := make([]test, 2)
	tps1 := make([]*test, 2)
	for idx := 0; idx < 2; idx++ {
		ts1[idx].A = "a"
		ts1[idx].B = 1
		tps1[idx] = &test{
			A: "a",
			B: 1,
		}
	}
	ts1byte, _ := json.Marshal(ts1)
	tps1byte, _ := json.Marshal(tps1)
	fmt.Println(string(ts1byte))
	fmt.Println(string(tps1byte))
}

输出:

[{"A":"a","B":1},{"A":"a","B":1}]
[{"A":"a","B":1},{"A":"a","B":1}]

使用interface传递结构体

参考encoding/json

image.png

package main

import (
	"encoding/json"
	"fmt"
)

// Example is our main data structure used for JSON parsing
type Example struct {
	Name    string `json:"name"`
	Numbers []int  `json:"numbers"`
	Nested  Nested `json:"nested"`
}

type Example1 struct {
	Name    string `json:"name"`
	Numbers []int  `json:"numbers"`
}

// Nested is an embedded structure within Example*
type Nested struct {
	IsIt        bool   `json:"isit"`
	Description string `json:"description"`
}

func test(s string, i interface{}) {
	err := json.Unmarshal([]byte(s), &i)
	if err != nil {
		fmt.Printf("Error parsing JSON string - %s", err)
	}
	// Print the name
	fmt.Printf("Name is %v\n", i)
}
func main() {
	// Define a JSON string
	j := `{"name":"example","numbers":[1,2,3,4],"nested":{"isit":true,"description":"a nested json"}}`

	// Parse JSON string into data object
	data := &Example{}
	test(j, data)
	fmt.Printf("Name is %s\n", data.Name)

	data1 := &Example1{}
	test(j, data1)
	fmt.Printf("Name is %s\n", data.Name)

}

输出:

Name is &{example [1 2 3 4] {true a nested json}}
Name is example
Name is &{example [1 2 3 4]}
Name is example

文献

qvault.io/golang/json…

zhuanlan.zhihu.com/p/53125839

www.modb.pro/db/72704