Json
Json 的基本类型有:
- 布尔类型,例如 true 和 false
- 数字,例如 1.23
- 字符串,例如"张三"
- 数组,例如
["go","PHP"]
,每个元素之间用逗号隔开 - 对象,如下所示,每个元素之间用逗号隔开
{
"year":1980,
"name":"张三"
}
其中,go 和 json 的数据结构体对应关系如下所示。
bool, for JSON booleans
float64, for JSON numbers
int, for JSON 整数
string, for JSON strings
nil, for JSON null
// 这个都是接口类型,在获取值的是比较麻烦
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects // 因为json的对象里的值有各种类型,所以值要接口类型
// 例如,对于数组
[]int for [1,2,3]
[]string for ["red","green"]
[]struct{Title string} for [{"Title":"功夫足球"},{"Title":"功夫"}]
// 例如对于对象,通常用结构体会比较好,不同的字段类型和不同的json数据结构对应上
struct{
Title string
Release int
} for {"Title":"功夫足球","Release":2004}
// 但是对应动态key的json,还是得用map,以下的年份作为动态key
map[string]struct{Title string} for {
"2000":{"Title":"功夫足球"},
"2004":{"Title":"功夫"},
}
编码
Go 中使用 marshal ,把结构体转为 json,如下所示:
type Movie struct {
Title string
Year int `json:"released"`
Color bool `json:"color,omitempty"`
Actors []string
}
func main() {
var movies = []Movie{
{
Title: "功夫足球",
Year: 2001,
Color: true,
Actors: []string{"周星驰"},
},
{
Title: "功夫",
Year: 2004,
Color: false,
Actors: []string{"周星驰"},
},
}
// data, err := json.Marshal(movies)
data, err := json.MarshalIndent(movies, "", " ")// 可缩进格式化输出json
if err != nil {
log.Fatalf("Json Marshal failed:%s", err)
}
fmt.Printf("%s", data)
}
将得到如下结果,其中有几个需要注意的是:
- 默认是将结构体成员的名称作为 json 对象里的字段的名称
- 只有可导出的成员变量才会被转为 json 字段,所以结构体的字段都要大写开头
- 在字段后面可以指定成员标签,成员标签可以是任意字符串,但是按照习惯,是由一串由空格分开的键值对
key:"value"
组成。例如json:"released"
,指定结构体成员对应 Json 字段的名字 - Color 成员标签有另外一个参数 omitempty,它表示这个成员的值是其零值或者为空,则不输出这个成员到 Json 中。
[
{
"Title": "功夫足球",
"released": 2001,
"color": true,
"Actors": [
"周星驰"
]
},
{
"Title": "功夫",
"released": 2004,
"Actors": [
"周星驰"
]
}
]
解码
Go 中使用 unmarshal 来将 json 字符串解码为 go 数据结构。通过合理的定义 Go 的数据结构,我们可以选择哪部分 json 数据解码到结构体对象中,哪些数据可以丢弃。例如下例中,我们只需要解码 title 字段。
jsonStr := `[{"Title":"功夫足球","released":2001,"color":true,"Actors":["周星驰"]},{"Title":"功夫","released":2004,"Actors":["周星驰"]}]`
var titles []struct{ Title string }
// 此时title还是nil,函数会先申请一个值并使指针指向它
if err := json.Unmarshal([]byte(jsonStr), &titles); err != nil {
log.Fatalf("Json 反序列化失败:%s", err)
}
fmt.Println(titles)
需要注意的是:
- 结构体的成员的名称的首字母必须大写
- Json 字符串的字段名称并不需要大写开头,关联到结构体字段的时候,是忽略大小写的
- 只有在 json 字段中有下划线,go 里没有下划线的时候,才需要使用成员标签定义
- json 中有的字段,结构体中可以没有,结构体中有的字段,json 中可以没有,但是一旦字段匹配上,字段的类型一定要一致,不然会异常
解码除了使用 unmarshal 。还可以使用 NewDecoder 进行解析
json.NewDecoder 是从一个流里面直接进行解码,代码精干,可以从 http 连接与 socket 连接的读取与写入,或者也可以从文件读取,速度比较快
result := new(map[string]interface{})
var json_str = `{"bool":true,"str":"你好"}` // 可以是 http 连接的流
resultType := reflect.TypeOf(result)
if resultType.Kind() != reflect.Ptr {
fmt.Println("result 对象必须是指针!")
return
}
decode := json.NewDecoder(bytes.NewBufferString(json_str))
// 防止 int64 丢失精度
decode.UseNumber()
if err := decode.Decode(result); err != nil {
fmt.Println("解析失败!")
return
}
fmt.Println(result)