10.4. Json

JSON是一种常见的数据交换格式,也是后端编写RESTful风格API时常采用的数据格式。JSON数据格式通常包含两个操作:

  • 序列化(把对象转换成JSON数据类型)

  • 反序列化(把JSON数据类型转换成对象),这是两个互逆的操作。

    在Go语言中,json操作的内置库名为encoding/json,常见的用法如图

10.4.1. 1.Json示例

package main

import (
    "encoding/json"
    "fmt"
)

type JsonExample struct {
    //序列化后显示name,如果为空就不显示
    Name   string `json:"name,omitempty"`
    //序列化后显示age
    Age    int    `json:"age"`
    //序列化后显示university
    School string `json:"university"`
}

//序列化
func JsonMarshal() {
    var jex JsonExample
    jex = JsonExample{
        Name:   "Go",
        Age:    10,
        School: "Google",
    }
    by, _ := json.Marshal(jex)
    fmt.Println(string(by))

}

//反序列化
func JsonUnmarshal() {

    var v JsonExample

    by := []byte(`{"name":"Go","age":10, "university":"google"}`)

    json.Unmarshal(by, &v)
    fmt.Println(v)

    var vother JsonExample
    byOther := []byte(`{"name":"","age":10, "school":"google"}`)
    json.Unmarshal(byOther, &vother)
    fmt.Println(vother)
}

func main() {
    JsonMarshal()
    JsonUnmarshal()
}

输出

{"name":"Go","age":10,"university":"Google"}
{Go 10 google}
{ 10 }

使用过程中需注意以下几点:

  • 如果知道反序列化之后的具体结构,那么应该先定义一个符合反序列之后的结构体。

  • 如果不知道反序列化之后的具体结构,那么应该使用interface来表示任意类型。

  • 结构体定义时的标签指定序列化之后的显示,比如上文的json:“name, omitempty”,序列化之后该字段显示name,如果为空值,就不显示(omitempty)。

10.4.2. 2.Json序列化与反序列化

import "encoding/json"

json包实现了json对象的编解码。

  • Marshal函数

  • Unmarshal函数

Marshal函数返回v的json编码:

type ColorGroup struct {
    ID     int
    Name   string
    Colors []string
}

group := ColorGroup{
    ID:     1,
    Name:   "Reds",
    Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}

b, err := json.Marshal(group)
if err != nil {
    fmt.Println("error:", err)
}

os.Stdout.Write(b)
//{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}

Unmarshal函数解析json编码的数据并将结果存入v指向的值:

var jsonBlob = []byte(`[
        {"Name": "Platypus", "Order": "Monotremata"},
        {"Name": "Quoll",    "Order": "Dasyuromorphia"}
    ]`)

type Animal struct {
    Name  string
    Order string
}

var animals []Animal
err := json.Unmarshal(jsonBlob, &animals)

if err != nil {
    fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)
//[{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name string `json:"userName"`
    Age  int
}


func main() {
    u1 := &User{Name: "nick", Age: 18}

    u1Str, err := json.Marshal(u1)
    fmt.Println(err)
    fmt.Println(u1Str)

    var u User
    err = json.Unmarshal([]byte(u1Str), &u)
    fmt.Println(err)
    fmt.Println(u)

}

2.1 struct tag介绍:

// 字段被本包忽略
Field int `json:"-"`

// 字段在json里的键为"myName"
Field int `json:"myName"`

// 字段在json里的键为"myName"且如果字段为空值将在对象中省略掉
Field int `json:"myName,omitempty"`

// 字段在json里的键为"Field"(默认值),但如果字段为空值会跳过;注意前导的逗号
Field int `json:",omitempty"`

2.2 time时间处理

type TimeBirthday time.Time

func (obj TimeBirthday) MarshalJSON() ([]byte, error) {
    seconds := time.Time(obj).Format("2006-01-02")
    return []byte(fmt.Sprintf(`"%s"`, seconds)), nil
}

type Account struct {
    Birthday     TimeBirthday `json:"birthday"`
    LastLoginTime  time.Time `json:"-"`
}

10.4.3. 3.应用案例-Json序列化

这里我们介绍一下结构体、map和切片的序列化,其它数据类型的序列化类似。

3.1 Marshal函数

代码示例

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

//定义一个结构体
type Monster struct {
    Name     string `json:"monster_name"` //反射机制
    Age      int    `json:"monster_age"`
    Birthday string //....
    Sal      float64
    Skill    string
}

func testStruct() {
    //演示
    monster := Monster{
        Name:     "牛魔王",
        Age:      500,
        Birthday: "2011-11-11",
        Sal:      8000.0,
        Skill:    "牛魔拳",
    }

    //将monster 序列化
    data, err := json.Marshal(&monster) //..
    if err != nil {
        fmt.Printf("序列号错误 err=%v\n", err)
    }
    //输出序列化后的结果
    fmt.Printf("monster序列化后=%v\n", string(data))

    // 创建文件,将json数据写入到文件中
    file := "info.json"
    filePtr, err1 := os.Create(file)
    if err1 != nil {
        fmt.Println("文件创建失败", err.Error())
        return
    }
    defer filePtr.Close()
    // 创建Json编码器
    encoder := json.NewEncoder(filePtr)
    err = encoder.Encode(string(data))
    if err != nil {
        fmt.Println("编码错误", err.Error())
    } else {
        fmt.Printf("json 文件 %s 编码成功\n", file)
    }
}

//将map进行序列化
func testMap() {
    //定义一个map
    var a map[string]interface{}
    //使用map,需要make
    a = make(map[string]interface{})
    a["name"] = "红孩儿"
    a["age"] = 30
    a["address"] = "洪崖洞"

    //将a这个map进行序列化
    //将monster 序列化
    data, err := json.Marshal(a)
    if err != nil {
        fmt.Printf("序列化错误 err=%v\n", err)
    }
    //输出序列化后的结果
    fmt.Printf("a map 序列化后=%v\n", string(data))

}

//演示对切片进行序列化, 我们这个切片 []map[string]interface{}
func testSlice() {
    var slice []map[string]interface{}
    var m1 map[string]interface{}
    //使用map前,需要先make
    m1 = make(map[string]interface{})
    m1["name"] = "jack"
    m1["age"] = "7"
    m1["address"] = "北京"
    slice = append(slice, m1)

    var m2 map[string]interface{}
    //使用map前,需要先make
    m2 = make(map[string]interface{})
    m2["name"] = "tom"
    m2["age"] = "20"
    m2["address"] = [2]string{"墨西哥", "夏威夷"}
    slice = append(slice, m2)

    //将切片进行序列化操作
    data, err := json.Marshal(slice)
    if err != nil {
        fmt.Printf("序列化错误 err=%v\n", err)
    }
    //输出序列化后的结果
    fmt.Printf("slice 序列化后=%v\n", string(data))

}

//对基本数据类型序列化,对基本数据类型进行序列化意义不大
func testFloat64() {
    var num1 float64 = 2345.67

    //对num1进行序列化
    data, err := json.Marshal(num1)
    if err != nil {
        fmt.Printf("序列化错误 err=%v\n", err)
    }
    //输出序列化后的结果
    fmt.Printf("num1 序列化后=%v\n", string(data))
}

func main() {
    //演示将结构体, map , 切片进行序列号
    testStruct()
    testMap()
    testSlice()   //演示对切片的序列化
    testFloat64() //演示对基本数据类型的序列化
}

10.4.4. 4.应用案例-JSON反序列化

4.1 Unmarshal函数

代码示例

我们演示json字符串反序列化成结构体、map和切片

package main
import (
    "fmt"
    "encoding/json"
)

//定义一个结构体
type Monster struct {
    Name string
    Age int
    Birthday string //....
    Sal float64
    Skill string
}


//演示将json字符串,反序列化成struct
func unmarshalStruct() {
    //说明str 在项目开发中,是通过网络传输获取到.. 或者是读取文件获取到
    str := "{\"Name\":\"牛魔王~~~\",\"Age\":500,\"Birthday\":\"2011-11-11\",\"Sal\":8000,\"Skill\":\"牛魔拳\"}"

    //定义一个Monster实例
    var monster Monster

    err := json.Unmarshal([]byte(str), &monster)
    if err != nil {
        fmt.Printf("unmarshal err=%v\n", err)
    }
    fmt.Printf("反序列化后 monster=%v monster.Name=%v \n", monster, monster.Name)

}
//将map进行序列化
func testMap() string {
    //定义一个map
    var a map[string]interface{}
    //使用map,需要make
    a = make(map[string]interface{})
    a["name"] = "红孩儿~~~~~~"
    a["age"] = 30
    a["address"] = "洪崖洞"

    //将a这个map进行序列化
    //将monster 序列化
    data, err := json.Marshal(a)
    if err != nil {
        fmt.Printf("序列化错误 err=%v\n", err)
    }
    //输出序列化后的结果
    //fmt.Printf("a map 序列化后=%v\n", string(data))
    return string(data)

}

//演示将json字符串,反序列化成map
func unmarshalMap() {
    //str := "{\"address\":\"洪崖洞\",\"age\":30,\"name\":\"红孩儿\"}"
    str := testMap()
    //定义一个map
    var a map[string]interface{}

    //反序列化
    //注意:反序列化map,不需要make,因为make操作被封装到 Unmarshal函数
    err := json.Unmarshal([]byte(str), &a)
    if err != nil {
        fmt.Printf("unmarshal err=%v\n", err)
    }
    fmt.Printf("反序列化后 a=%v\n", a)

}

//演示将json字符串,反序列化成切片
func unmarshalSlice() {
    str := "[{\"address\":\"北京\",\"age\":\"7\",\"name\":\"jack\"}," +
        "{\"address\":[\"墨西哥\",\"夏威夷\"],\"age\":\"20\",\"name\":\"tom\"}]"

    //定义一个slice
    var slice []map[string]interface{}
    //反序列化,不需要make,因为make操作被封装到 Unmarshal函数
    err := json.Unmarshal([]byte(str), &slice)
    if err != nil {
        fmt.Printf("unmarshal err=%v\n", err)
    }
    fmt.Printf("反序列化后 slice=%v\n", slice)
}

func main() {
    unmarshalStruct()
    unmarshalMap()
    unmarshalSlice()
}

10.4.5. 5. Json编解码案例1

准备测试数据

data.json

{
  "data": {
    "directors": [
      "奉俊昊"
    ],
    "rate": "8.9",
    "cover_x": 1500,
    "star": "45",
    "title": "寄生虫",
    "url": "https://movie.douban.com/subject/27010768/",
    "casts": [
      "宋康昊",
      "李善均",
      "赵汝贞",
      "崔宇植",
      "朴素丹"
    ]
  }
}

json.go

package jsonExplain

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
)

type ResultForJSON struct {
    Data struct {
        Directors []string `json:"directors"`
        Rate      string   `json:"rate"`
        Cover     int      `json:"cover_x"`
        Star      string   `json:"star"`
        Title     string   `json:"title"`
        URL       string   `json:"url"`
        Casts     []string `json:"casts"`
    } `json:"data"`
}


// 解析json
func ParseJSON() {
    file, err := ioutil.ReadFile("data.json")
    if err != nil {
        log.Println(err)
        return
    }
    var result ResultForJSON
    err = json.Unmarshal(file, &result)
    if err != nil {
        log.Println(err)
        return
    }
    fmt.Println(result)
}

// 封装json
func MarshalJSON() {
    var object ResultForJSON
    object.Data.Directors = []string{"郑伟文", "陈家霖"}
    object.Data.Casts = []string{"肖战", "王一博", "孟子义", "宣璐", "于斌"}
    object.Data.Title = "陈情令"
    object.Data.Rate = "7.7"
    object.Data.Star = "40"
    object.Data.Cover = 3000
    object.Data.URL = "https://movie.douban.com/subject/27195020/"

    content, err := json.Marshal(object)
    if err != nil {
        log.Println(err)
        return
    }
    fmt.Println(string(content))

}

10.4.6. 6. Json编解码案例2

6.1 JSON序列化

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type Movie struct {
    Title  string
    Year   int  `json:"released"`
    Color  bool `json:"color,omitempty"`
    Actors []string
}

var movies = []Movie{
    {Title: "Casablanca", Year: 1942, Color: false,
        Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
    {Title: "Cool Hand Luke", Year: 1967, Color: true,
        Actors: []string{"Paul Newman"}},
    {Title: "Bullitt", Year: 1968, Color: true,
        Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
}

func main() {
    // Marshal
    data, err := json.Marshal(movies)
    if err != nil {
        log.Fatalf("JSON marshaling failed: %s", err)
    }
    fmt.Printf("%s\n", data)
}

这种类型的结构体最适合JSON,无论是Go转JSON还是从JSON转为Go对象都很容易,转换为JSON结构,使用json.Marshal函数来实现,输出结果:

[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingrid Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Actors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"Actors":["Steve McQueen","Jacqueline Bisset"]}]

这种紧凑的包含了所有信息,但是难以阅读。为了方便阅读可以使用 json.MarshalIndent函数来输出整齐格式化的结果。

这个函数有两个参数,一个是定义每行输出的前缀字符串,另外一个是定义缩进的字符串。

// MarshalIndent
data, err := json.MarshalIndent(movies, "", "    ")
if err != nil {
    log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)

上面的代码输出:

[
    {
        "Title": "Casablanca",
        "released": 1942,
        "Actors": [
            "Humphrey Bogart",
            "Ingrid Bergman"
        ]
    },
    {
        "Title": "Cool Hand Luke",
        "released": 1967,
        "color": true,
        "Actors": [
            "Paul Newman"
        ]
    },
    {
        "Title": "Bullitt",
        "released": 1968,
        "color": true,
        "Actors": [
            "Steve McQueen",
            "Jacqueline Bisset"
        ]
    }
]

6.2 JSON反序列化

将JSON字符串解码为Go数据结构,使用json.Unmarshal函数实现。

mo := &movies
// Unmarshal
if err := json.Unmarshal(data, &mo); err != nil {
    log.Fatalf("JSON unmarshaling failed: %s", err)
}
// fmt.Println(*mo)
for _, m := range *mo {
    fmt.Println(m.Title)
}

10.4.7. 7. Json转Go结构体工具

https://www.mogublog.net/app/go-json/

10.4.8. 8.参考文献

https://www.yuque.com/petrels/ugpuss/qu38iy