TOC

Golang JSON

Golang 有一个 encoding/json 标准库,在多数情况下已经够用了。

注意:因为 Golang 是一种静态类型的语言,所以解析 JSON 字符串必须了解里面的数据结构。
如果是用惯了 JS、Python、PHP 等动态语言的话,到这里会有些郁闷,怎么解析一个 JSON 还这么复杂。

接口

  • 序列化
    • func Marshal(v interface{}) ([]byte, error)
    • func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
    • func NewEncoder(w io.Writer) *Encoder
  • 反序列化
    • func Unmarshal(data []byte, v interface{}) error
    • func NewDecoder(r io.Reader) *Decoder

最简单的情况

  1. struct 定义出符合要求的类型对应 JSON 对象
  2. 如果非常简单,类型一致,可能只需要用数组和 map 就可以了
package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    json_str := `[{"name":"admin","gender":"male"},{"name":"staff001","gender":"female"}]`
    json_bytes := []byte(json_str)
    json_map := make([](map[string]string), 10)
    err := json.Unmarshal(json_bytes, &json_map)
    if err != nil {
        fmt.Println("UmarshalError:", err)
        return
    }
    fmt.Println(json_map)
    s, err := json.Marshal(json_map)
    fmt.Println(string(s))
}

动态 Key

延迟解析 json.RawMessage

type Responce {
    code string
    msg  string
    data *json.RawMessage
}

omitempty

type User struct {
    Name     string `json:"name"`
    Age      int    `json:"age,omitempty"`
    Password string `json:"-"` // JSON 序列化时忽略该字段
}

开发者应该清楚 omitempty 判断控制逻辑对序列化的影响:

  1. 如果字段是 struct 类型,则该字段默认值就不会被判定为空,emitempty 失效
  2. 如果字段的值正好就是默认值,比如 int 类型值为 0 的时候,emitempty 会干掉这个字段

如有必要,解决方法就是改成指针的方式。

第三方库

更详细的对比参考《Go JSON 第三方库》。

应用

编码与解码

package main

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

type AppConfig struct {
    Name    string  `json:"name"`
    Version float64 `json:"version"`
}

const configPath = "config.json"

func main() {
    config, err := readConfig()
    if err != nil {
        fmt.Printf("读配置文件错误:%s\n", err)
        return
    }
    fmt.Printf("配置:%#v\n", config)
}

func readConfig() (AppConfig, error) {
    fileBytes, err := ioutil.ReadFile(configPath)
    if err != nil {
        return AppConfig{}, err
    }
    var config AppConfig
    err = json.Unmarshal(fileBytes, &config)
    if err != nil {
        return AppConfig{}, err
    }
    return config, nil
}

验证字符串是否是 JSON

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    s := `{"name": "Jimmy", }`
    fmt.Printf("%+v", json.Valid([]byte(s)))
}

// 多了一个逗号
// false

但是需要注意,字符串,数字,true/false,都是合法的 JSON 字符串。
真用到的时候,可能需要结合业务,进一步判断字符串类型。

参考资料与拓展阅读

如果你有魔法,你可以看到一个评论框~