在了解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