在了解golang如何处理json数据之前,先知道一下术语说明:

  • 数据结构 ===> 指定格式 = 序列化 或 编码    (传输之前)
  • 指定格式 ===> 数据格式 = 反序列化 或 解码 (传输之后)

序列化是在内存中把数据转换成指定格式(data -> string),反之亦然(string -> data structure) 

编码也是一样的,只是输出了一个数据流(实现了io.Writer接口);解码是从一个数据流(实现了io.Reader)输出到一个数据结构

GO语言的json包可以让你在程序中方便的读取和写入JSON数据。

import  "encoding/json"

 json.Marshal()函数签名是 func Marshal(v interface{}) ([]byte, error) 。出于安全考虑,在web应用中最好使用json.MarshalforHTML()函数,其对数据执行HTML转码,所以文本可以被安全地镶嵌在HTML<script>标签中。

json.NewEncoder() 的函数签名是 func NewEncoder(w  io.Writer) *Encoder, 返回的Encoder类型的指针可调用方法

Encode(v  interface{}),  将数据对象v的json编码写入io.Writer  w中。

JSON 与 Go 类型对应如下:

  • bool 对应 JSON 的 boolean
  • float64 对应 JSON 的 number
  • string 对应 JSON 的 string
  • nil 对应 JSON 的 null

 序列化举例:

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
)

type Address struct {
	Type    string
	City    string
	Country string
}

type VCard struct {
	FirstName string
	LastName  string
	Addresses []*Address
	Remark    string
}

func main() {
	pa := &Address{"private", "Aartselaar", "Belgium"}
	wa := &Address{"work", "Boom", "Belgium"}
	vc := VCard{"Jan", "Kersschot", []*Address{pa, wa}, "none"}
	// fmt.Printf("%v: \n", vc) // {Jan Kersschot [0x126d2b80 0x126d2be0] none}:
	// JSON format:
	js, _ := json.Marshal(vc)
	fmt.Printf("JSON format: %s", js)
	// using an encoder:
	file, _ := os.OpenFile("vcard.json", os.O_CREATE|os.O_WRONLY, 0666)
	defer file.Close()
	enc := json.NewEncoder(file)
	err := enc.Encode(vc)
	if err != nil {
		log.Println("Error in encoding json")
	}
}

 生成的数据类型为:

{
    "FirstName": "Jan",
    "LastName": "Kersschot",
    "Addresses": [{
        "Type": "private",
        "City": "Aartselaar",
        "Country": "Belgium"
    }, {
        "Type": "work",
        "City": "Boom",
        "Country": "Belgium"
    }],
    "Remark": "none"
}

反序列化:

UnMarshal() 的函数签名是 func Unmarshal(data  []byte, v interface{}) error  把JSON解码为数据结构。

json包使用 map[string]interface{} 和 []interface{} 储存任意的JSON对象和数组;其可以被反序列化为任何的JSON blob存储到接口值中。

来看这个 JSON 数据,被存储在变量 b 中:

b := []byte(`{"Name": "Wednesday", "Age": 6, "Parents": ["Gomez", "Morticia"]}`)

不用理解这个数据的结构,我们可以直接使用 Unmarshal 把这个数据编码并保存在接口值中:

var f interface{}
err := json.Unmarshal(b, &f)

f 指向的值是一个 map,key 是一个字符串,value 是自身存储作为空接口类型的值:

map[string]interface{} {
	"Name": "Wednesday",
	"Age":  6,
	"Parents": []interface{} {
		"Gomez",
		"Morticia",
	},
}

要访问这个数据,我们可以使用类型断言

m := f.(map[string]interface{})

我们可以通过 for range 语法和 type switch 来访问其实际类型:

for k, v := range m {
	switch vv := v.(type) {
	case string:
		fmt.Println(k, "is string", vv)
	case int:
		fmt.Println(k, "is int", vv)

	case []interface{}:
		fmt.Println(k, "is an array:")
		for i, u := range vv {
			fmt.Println(i, u)
		}
	default:
		fmt.Println(k, "is of a type I don’t know how to handle")
	}
}

通过这种方式,你可以处理未知的 JSON 数据,同时可以确保类型安全。

相反,如果我们事先知道JSON数据,我们可以定义一个适当的结构并对JSON数据反序列化。

type FamilyMember struct {
	Name    string
	Age     int
	Parents []string
}

并对其反序列化:

var m FamilyMember
err := json.Unmarshal(b, &m)

 程序实际上式分配了一个新的切片。这是一个典型的反序列化引用类型(指针、切片和map)的例子。

 

编码和解码流

json 包提供 Decoder 和 Encoder 类型来支持常用 JSON 数据流读写。NewDecoder 和 NewEncoder 函数分别封装了 io.Reader 和 io.Writer 接口。

func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder

要想把 JSON 直接写入文件,可以使用 json.NewEncoder 初始化文件(或者任何实现 io.Writer 的类型),并调用 Encode();反过来与其对应的是使用 json.NewDecoder 和 Decode() 函数:

func NewDecoder(r io.Reader) *Decoder
func (dec *Decoder) Decode(v interface{}) error