Go如何处理JSON


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]}
JSON和Go之间映射数据类型

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写一篇博客。

Content-Type四种常见取值


文章作者: xuxiangfei
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 xuxiangfei !
  目录