Go如何处理JSON
参考《Go语言入门经典》第20章 处理JSON
JSON作为前后端交互的通用格式,在日常开发中经常会使用到,尤其对于后端开发者要弄清楚如何完成JSON的编码与解码,以及如何通过HTTP请求的读写JSON数据。
1 JSON简介
JSON(JavaScript Object Notion)即JavaScript对象表示法,类似于txt是一种数据存储格式。最初是JavaScript的一个子集,JavaScript天然支持JSON。
JSON数据主要以key-value的形式进行组织,包含在一个JSON对象中,支持7种数据类型(6种基本类型+1种空类型)
{
"name": "zhangsan", // String
"sex": true, // Boolean
"age": 18, // Number
"height": 176.5, // Number
"hobby": ["pingpon", "basketball"], // Array
"father": {"name": "lisi", "age": 48}, // Object
"grilfriend": null // NULL
}
注意:整数和浮点数都是Number
2 Go处理JSON
Go语言十分适合服务端开发,提供了 encoding/json
包用于JSON数据的编码和解码。
2.1 编码
encoding/json包提供了函数 Marshal
,返回编码后的字节数组,用于将Go数据编码为JSON。
需要注意的是struct字段的首字母需要大写,不然无法编码。比如下面Person对象中的age和hobby无法导出。
type Person struct {
Name string
age int
hobby []string
}
func main() {
hobby := []string{"pingpon", "basketball"}
p := Person{
Name: "zhangsan",
age: 18,
hobby: hobby,
}
fmt.Printf("%+v\n", p)
jsonByteData, _ := json.Marshal(p)
fmt.Println(string(jsonByteData))
}
// {Name:zhangsan age:18 hobby:[pingpon basketball]}
// {"Name":"zhangsan"}
2.1.1 struct字段设置可导出
把Person的定义改成如下形式,即可全部导出。
type Person struct {
Name string
Age int
Hobby []string
}
// {Name:zhangsan Age:18 Hobby:[pingpon basketball]}
// {"Name":"zhangsan","Age":18,"Hobby":["pingpon","basketball"]}
但是这样有个问题,导出json数据的key是首字母大写,如果需要小写怎么办呢?
2.1.2 自定义导出的key
给字段加上标签tag即可自定义导出的key
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Hobby []string `json:"hobbies"` // 自定义
}
// {Name:zhangsan Age:18 Hobby:[pingpon basketball]}
// {"name":"zhangsan","age":18,"hobbies":["pingpon","basketball"]}
2.1.3 空类型
如果go数据为nil,那么编码出来的JSON也是空类型, 想要忽略空类型的话,在struct的tag里添加omitempty即可
// 不忽略空类型
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Hobby []string `json:"hobbies"`
}
p := Person{
Name: "zhangsan",
Age: 18,
Hobby: nil,
}
fmt.Printf("%+v\n", p)
jsonByteData, _ := json.Marshal(p)
fmt.Println(string(jsonByteData))
// {Name:zhangsan Age:18 Hobby:[]}
// {"name":"zhangsan","age":18,"hobbies":null}
// -----------------------------------------------------
// 忽略空类型 omitempty
type Person struct {
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
Hobby []string `json:"hobbies,omitempty"`
}
p := Person{
Name: "zhangsan",
Age: 18,
Hobby: nil,
}
fmt.Printf("%+v\n", p)
jsonByteData, _ := json.Marshal(p)
fmt.Println(string(jsonByteData))
// {Name:zhangsan Age:18 Hobby:[]}
// {"name":"zhangsan","age":18}
2.2 解码
encoding/json包提供了函数 Unmarshal
,接受一个字节切片以及一个指定要将数据解码为何种格式的接口,用于将JSON解码为Go数据。
type Person struct {
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
Hobby []string `json:"hobbies,omitempty"`
}
func main() {
jsonString := `{"name":"zhangsan","age":18,"hobbies":["pingpon","basketball"]}`
fmt.Println(jsonString)
p := Person{}
err := json.Unmarshal([]byte(jsonString), &p)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", p)
}
// {"name":"zhangsan","age":18,"hobbies":["pingpon","basketball"]}
// {Name:zhangsan Age:18 Hobby:[pingpon basketball]}

3 通过HTTP读写JSON
3.1 读出JSON
在Go语言中,通过HTTP请求获取JSON时,收到的数据为流而不是字符串或字节切片。在这种情况下,应使用encoding/json包中的另一个方法 NewDecoder
。
NewDecoder
函数接受一个io.Reader(这正是http.Get返回的类型),并返回一个Decoder。通过对返回的Decoder调用方法Decode,可将数据解码为结构体。
type User struct {
Id int64 `json:"id,omitempty"`
Name string `json:"name,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
}
func main() {
res, err := http.Get("https://api.github.com/users/xxf0512")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var u User
err = json.NewDecoder(res.Body).Decode(&u)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", u)
}
// {Id:49829039 Name:xxf CreatedAt:2019-04-21T06:14:00Z}
3.2 写入JSON
模拟发送一个post请求,其中写入JSON
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
type Weather struct {
UserId int `json:"userId"`
Body string `json:"body"`
Title string `json:"title"`
}
func main() {
w := Weather{
UserId: 100,
Body: "This is body",
Title: "This is title",
}
str, _ := json.Marshal(w)
postData := string(str)
fmt.Println("postData: ", postData)
res, err := http.Post("https://jsonplaceholder.typicode.com/posts", "application/x-www-form-urlencoded", strings.NewReader(postData))
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}
这里Content-Type是application/x-www-form-urlencoded,关于Content-Type可以参考下面的文章,后面再就Content-Type写一篇博客。