kevinyan815 / gocookbook

go cook book

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Golang解析JSON

kevinyan815 opened this issue · comments

之前一直写一些动态语言,觉得解析JSON还是很简单的,往往只需要几行代码就能拿到解析好的JSON对象。Go语言自带的json包可以让你在程序中方便的读取和写入 JSON 数据。生成JSON场景相对简单一些,json.Marshal()会根据传入的结构体生成JSON数据。解析JSON会把数据解析到结构体中,由于JSON格式的自由组合的特点,尤其是那些结构复杂的JSON数据对新手来说声明接受JSON数据的结构体类型就会陷入不知从何下手的困扰。 最近工作中由于要解析JS和PHP程序持久化的JSON数据,大概花了两个下午才搞清楚针对常见的JSON数据应该如何声明结构体类型,下面分别说明一下。

I have been writing some dynamic languages ​​before, and I think it is very simple to parse JSON. It usually takes only a few lines of code to get the parsed JSON object. The json package that comes with the Go language allows you to easily read and write JSON data in your program. Generating a JSON scene is relatively simple, and json.Marshal() will generate JSON data based on the incoming structure. Parsing JSON will parse the data into the structure. Due to the free combination of JSON format, especially those JSON data with complex structure, the structure type that declares the JSON data for the novice will fall into the trap of not knowing where to start. In recent work, it took about two afternoons to figure out how to declare the structure type for common JSON data, because it is necessary to parse the JSON data of JS and PHP programs.

解析简单JSON(Parse simple json)

package main

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

func main() {
    type FruitBasket struct {
        Name    string 
        Fruit   []string
        Id      int64 `json:"ref"`// 声明对应的json key
        Created time.Time
    }

    jsonData := []byte(`
    {
        "Name": "Standard",
        "Fruit": [
             "Apple",
            "Banana",
            "Orange"
        ],
        "ref": 999,
        "Created": "2018-04-09T23:00:00Z"
    }`)

    var basket FruitBasket
    err := json.Unmarshal(jsonData, &basket)
    if err != nil {
         fmt.Println(err)
    }
    fmt.Println(basket.Name, basket.Fruit, basket.Id)
    fmt.Println(basket.Created)
}

说明: 由于json.UnMarshal()方法接收的是字节切片,所以首先需要把JSON字符串转换成字节切片c := []byte(s)
Playground url: https://play.golang.org/p/mcB6Kb6zCHE

解析内嵌对象的JSON (JSON with embed object)

把上面的Fruit值如果改成字典 "Fruit" : {"Name", "Apple", "PriceTag": "$1"}, 变成:

    jsonData := []byte(`
    {
        "Name": "Standard",
        "Fruit" : {"Name": "Apple", "PriceTag": "$1"},
        "ref": 999,
        "Created": "2018-04-09T23:00:00Z"
    }`)

那么结构体类型应该这么声明

type Fruit struct {
    Name string `json":Name"`
    PriceTag string `json:"PriceTag"`
}

type FruitBasket struct {
    Name    string 
    Fruit   Fruit
    Id      int64 `json:"ref"`// 声明对应的json key
    Created time.Time
}

Playground url: https://play.golang.org/p/dqw6tLb4JWm

解析内嵌对象数组的JSON(Embed Array of Object)

如果上面JSON对象里的Fruit值现在变成了

"Fruit" : [
	{
	    "Name": "Apple",
            "PriceTag": "$1"
	},
	{
	    "Name": "Pear",
	    "PriceTag": "$1.5"
	}
]

这种情况也简单把解析JSON的结构体做如下更改,把Fruit字段类型换为 []Fruit即可

type Fruit struct {
	Name string `json:"Name"`
	PriceTag string `json:"PriceTag"`
}

type FruitBasket struct {
    Name    string 
    Fruit   []Fruit
    Id      int64 `json:"ref"`// 声明对应的json key
    Created time.Time
}

解析具有动态Key的对象(Parse a JSON object with dynamic key)

下面再做一下复杂的变通,如果把上面的对象数组变为Key为水果ID的对象(object of object)比如

"Fruit" : {
	"1": {
		"Name": "Apple",
		"PriceTag": "$1"
	},
	"2": {
		"Name": "Pear",
		"PriceTag": "$1.5"
	}
}

每个Key的名字在声明结构体的时候是不知道值的,这样该怎么声明呢,答案是把Fruit字段的类型声明为一个key为string类型值为Fruit类型的map

type Fruit struct {
    Name string `json:"Name"`
    PriceTag string `json:"PriceTag"`
}

type FruitBasket struct {
    Name    string 
    Fruit   map[string]Fruit
    Id      int64 `json:"ref"`// 声明对应的json key
    Created time.Time
}

示例代码

package main

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

func main() {
    type Fruit struct {
        Name string `json:"Name"`
        PriceTag string `json:"PriceTag"`
    }

    type FruitBasket struct {
        Name    string 
        Fruit   map[string]Fruit
        Id      int64 `json:"ref"`// 声明对应的json key
        Created time.Time

    }    
    jsonData := []byte(`
    {
        "Name": "Standard",
        "Fruit" : {
	    "1": {
		"Name": "Apple",
		"PriceTag": "$1"
	    },
	    "2": {
		"Name": "Pear",
		"PriceTag": "$1.5"
	    }
        },
        "ref": 999,
        "Created": "2018-04-09T23:00:00Z"
    }`)

    var basket FruitBasket
    err := json.Unmarshal(jsonData, &basket)
    if err != nil {
         fmt.Println(err)
    }
    for _, item := range basket.Fruit {
	fmt.Println(item.Name, item.PriceTag)
    }
}

Playground url: https://play.golang.org/p/fh8JKa6pKJS

解析包含任意层级的数组和对象的JSON数据(arbitrary arrays and objects)

针对包含任意层级的JSON数据,声明结构体类型比较困难,encode/son包还提供另外一种方法来解析JSON数据。
encoding/json包使用:

  • map[string]interface{} 存储JSON对象
  • []interface 存储JSON数组

json.Unmarshl 将会把任何合法的JSON数据存储到一个interface{}类型的值,通过使用空接口类型我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言。

示例代码:

jsonData := []byte(`{"Name":"Eve","Age":6,"Parents":["Alice","Bob"]}`)

var v interface{}
json.Unmarshal(jsonData, &v)
data := v.(map[string]interface{})

for k, v := range data {
    switch v := v.(type) {
    case string:
        fmt.Println(k, v, "(string)")
    case float64:
        fmt.Println(k, v, "(float64)")
    case []interface{}:
        fmt.Println(k, "(array):")
        for i, u := range v {
            fmt.Println("    ", i, u)
        }
    default:
        fmt.Println(k, v, "(unknown)")
    }
}

虽然将JSON数据存储到空接口类型的值中可以用来解析任意结构的JSON数据,但是在实际应用中发现还是有不可控的地方,比如将数字字符串的值转换成了float类型的值,所以经常会在运行时报类型断言的错误,所以在JSON结构确定的情况下还是优先使用结构体类型声明,将JSON数据到结构体中的方式来解析JSON。

Although storing JSON data in an empty interface can be used to parse JSON data of any structure, I found that there is still something uncontrollable, such as converting the value of a numeric string into a float value. Therefore, the type assertion error is often reported in the runtime, so in the case of JSON structure determination, parse the JSON data into a predefined struct is preferred.

用 Decoder解析数据流

// This example uses a Decoder to decode a stream of distinct JSON values.
func ExampleDecoder() {
	const jsonStream = `
	{"Name": "Ed", "Text": "Knock knock."}
	{"Name": "Sam", "Text": "Who's there?"}
	{"Name": "Ed", "Text": "Go fmt."}
	{"Name": "Sam", "Text": "Go fmt who?"}
	{"Name": "Ed", "Text": "Go fmt yourself!"}
`
	type Message struct {
		Name, Text string
	}
	dec := json.NewDecoder(strings.NewReader(jsonStream))
	for {
		var m Message
		if err := dec.Decode(&m); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s: %s\n", m.Name, m.Text)
	}
	// Output:
	// Ed: Knock knock.
	// Sam: Who's there?
	// Ed: Go fmt.
	// Sam: Go fmt who?
	// Ed: Go fmt yourself!
}