JSON是平时最常用的一种数据格式,那么Go怎么操作JSON呢?
说到JSON对它最多的操作就是解析某个JSON或者将数据组合生成一个JSON。
Go的数据类型 | 对应Json中的类型 |
---|---|
bool | JSON booleans |
float64 | JSON numbers |
string | JSON strings |
[]interface{} | JSON arrays |
map[string]interface{} | JSON objects |
nil | JSON null |
先说解析一个拿到的JSON
我们用到的包是Go内置的
import "encoding/json"
现在我们有一个JSON需要解析一下
{
"species": "pigeon",
"decription": "likes to perch on rocks"
}
在Go中,如果想要解析某个JSON,就必须要创建一个对应的结构体。具体的说就是根据JSON中的key & value
,创建一个拥有对应类型成员变量的struct
,just like this
type Bird struct {
Species string
Description string
}
上面json
中有species & decription
两个key,那么结构体也要有两个成员变量,而且注意类型都要一致
important
有注意到结构体中每个变量命名的特点吗?
都是大写字母开头,这一点是有原因的:
大写开头是说明将要使用的,接下来所关注的一些json中的字段,只有以大写字母开头的成员变量,才能解析与json中对应的key,如果一个成员变量以小写开头定义或者不写,都是不会被解析的。
然后就是解析了
birdJson := `{"species": "pigeon","description": "likes to perch on rocks"}`
var bird Bird
json.Unmarshal([]byte(birdJson), &bird)
fmt.Printf("Species: %s, Description: %s", bird.Species, bird.Description)
可以看到有一个JSON结构的变量birdJson
,使用json.Unmarshal([]byte(birdJson), &bird)
解析,这个函数第一个参数是要解析的对象,必须是[]byte类型,这里将一整串数据8位8位读取到byte类型切片中。第二个参数是目标变量,也就是存放解析好的json的,所以可以看到这里使用了先前定义好的Bird
类型,也就是与json的key想对应的struct
。
当然在实际的开发中,很多情况并不是单纯的只接受一个JSON,而是多个JSON组成的数组,所以知道如何解析JSON数组也是一个非常重要的。
假设现在有一个这样的JSON需要解析
[
{
"species": "pigeon",
"decription": "likes to perch on rocks"
},
{
"species":"eagle",
"description":"bird of prey"
}
]
只需要将上面var bird Bird
变为var bird []Bird
就可以了,对就是将一个Bird类型的变量变成Bird类型的数组即可
birdJson := `[{"species":"pigeon","decription":"likes to perch on rocks"},{"species":"eagle","description":"bird of prey"}]`
var birds []Bird
json.Unmarshal([]byte(birdJson), &birds)
fmt.Printf("Birds : %+v", birds)
//Birds : [{Species:pigeon Description:} {Species:eagle Description:bird of prey}]
然而简单的JSON往往也无法满足日常实际中的开发,一个复杂的JSON对象可能包含一个Object
类型的key
,就像这个
{
"species": "pigeon",
"decription": "likes to perch on rocks",
"dimensions": {
"height": 24,
"width": 10
}
}
解析这类的JSON也是同理,只需要在定义一个对应的struct
即可,like this
type Dimensions struct {
Height int
Width int
}
type Bird struct {
Species string
Description string
Dimensions Dimensions
}
然后解析的方法就完全一样了
birdJson := `{"species":"pigeon","description":"likes to perch on rocks", "dimensions":{"height":24,"width":10}}`
var birds Bird
json.Unmarshal([]byte(birdJson), &birds)
fmt.Printf(bird)
// {pigeon likes to perch on rocks {24 10}}
有时候并不像严格按照JSON中每个key
的名称来定义struct
中每个属性的名称,这个时候就可以用到filed tags
来解决这个问题
{
"birdType": "pigeon",
"what it does": "likes to perch on rocks"
}
别如上面这个JSON,如果要按照key
来定义结构体中的属性名称,那么可能就要定义一个WhatItDose
这样的属性...不是很好,所以可以这样写,来完成自定义的属性名
type Bird struct {
Species string `json:"birdType"`
Description string `json:"what it does"`
}
说完解析JSON,拼凑一个JSON则可以使用完全相反的过程来完成,想对应的要使用 json.Marshal()
这个函数
同样,先定义好对应的结构体
type Animal struct {
Name string `json:"name"`
Order string `json:"order"`
}
然后,将想要转成JSON的数据进行拼装,然后Marshl
var animals []Animal
animals = append(animals, Animal{Name: "Platypus", Order: "Monotremata"})
animals = append(animals, Animal{Name: "Quoll", Order: "Dasyuromorphia"})
jsonStr, err := json.Marshal(animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(jsonStr))
其实还有其他方法来组装,比如Gin框架中的c.JSON
我会在后续的例子中给出
Gin框架中使用JSON也是相当的方便
先给出我要POST的一段JSON
{
"name": "hqz",
"age" : "12",
"habit": "play tennis"
}
然后在代码中定义好对应的结构体,这些都和上述方法一致
type Info struct {
Name string `json:"name"`
Age string `json:"age"`
Habit string `json:"habit"`
}
然后接受request body
中POST过来的JSON,然后将得到的数据Bind,就可以拿到新鲜的JSON了,具体这个Bind之类的是什么,会在后续学习Gin框架中给出。
func testPage(c *gin.Context) {
i := &Info{}
c.Bind(i)
fmt.Println(i.Name)
c.JSON(http.StatusOK, i)
}
func main() {
router := gin.Default()
router.POST("/test", testPage)
router.Run()
}
可以看到c.JSON(http.StatusOK, i)
这个方法,接受两个参数,第一个参数是一个int
类型,就是状态码,第二个参数则是要返回的具体内容,是一个interface{}
具体为什么是interface我现在还不知道。
然后这样就把拿到的JSON返回了,这个过程适用于写API的时候。