[golang] json 处理

1,384 阅读3分钟
{
    "servers":[
        {
            "serverName":"Shanghai_VPN",
            "serverIP":"127.0.0.1"
        },
        {
            "serverName":"Beijing_VPN",
            "serverIP":"127.0.0.2"
        }
    ]
}

解析 json

解析到 struct

func main() {
    var s Serverslice
    str := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`
    json.Unmarshal([]byte(str), &s)
    fmt.Println(s)
}

type Server struct {
    ServerName string
    ServerIP   string
}

type Serverslice struct {
    Servers []Server
}
{[{Shanghai_VPN 127.0.0.1} {Beijing_VPN 127.0.0.2}]}

在解析的时候,如何将 json 数据与 struct 字段相匹配呢?例如 json 的 key 是 Foo,那么怎么找对应的字段呢?

  • 首先查找 tag 含有 Foo 可导出字段(首字母大写);
  • 其次查找字段名是 Foo 的可导出字段;
  • 最后查找类似 FOO 或者 FoO 这样的除了首字母之外其他大小写不敏感的可导出字段;

解析到 interface

如果不知道被解析的数据的格式,又应该如何来解析呢?
interface{} 可以用来存储任意数据类型的对象,JSON 包中采用 map[string]interface{}[]interface{} 结构来存储任意的 json 对象和数组。

func main() {
    b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)

    var f interface{}
    _ = json.Unmarshal(b, &f)

    // f = map[string]interface{}{
    // 	"Name": "Wednesday",
    // 	"Age":  6,
    // 	"Parents": []interface{}{
    // 		"Gomez",
    // 		"Morticia",
    // 	},
    // }

    m := f.(map[string]interface{})

    for k, v := range m {
        switch vv := v.(type) {
        case string:
            fmt.Println(k, "is string", vv)
        case int:
            fmt.Println(k, "is int", vv)
        case float64:
            fmt.Println(k, "is float64", vv)
        case []interface{}:
            fmt.Println(k, "is an array:")
            for i, u := range vv {
                fmt.Println(i, u)
            }
        default:
            fmt.Println(k, "is of a type I don't know how to handle")
        }
    }
}
Parents is an array:
0 Gomez
1 Morticia
Name is string Wednesday
Age is float64 6 

生成 json

func main() {
    var s Serverslice
    s.Servers = append(s.Servers, Server{ServerName: "Shanghai_VPN", ServerIP: "127.0.0.1"})
    s.Servers = append(s.Servers, Server{ServerName: "Beijing_VPN", ServerIP: "127.0.0.2"})
    b, err := json.Marshal(s)
    if err != nil {
            fmt.Println("json err:", err)
    }
    fmt.Println(string(b))
}

type Server struct {
    ServerName string `json:"server_name"`
    ServerIP   string `json:"server_ip"`
}

type Serverslice struct {
    Servers []Server `json:"servers"`
}
{"servers":[{"server_name":"Shanghai_VPN","server_ip":"127.0.0.1"},{"server_name":"Beijing_VPN","server_ip":"127.0.0.2"}]}

如果字段类型是 bool、string、int 或 int64 等,而 tag 中带有 ",string" 选项,那么这个字段在输出到 json 的时候会把该字段对应的值转换成 json 字符串。

type Server struct {
    // ID 不会导出到 JSON 中
    ID int `json:"-"`

    ServerName  string `json:"serverName"`
    // ServerName2 的值会进行二次 JSON 编码
    ServerName2 string `json:"serverName2,string"`

    // 如果 ServerIP 为空,则不输出到 json 串中
    ServerIP string `json:"serverIP,omitempty"`
}

s := Server{
    ID:          3,
    ServerName:  `Go "1.0" `,
    ServerName2: `Go "1.0" `,
    ServerIP:    ``,
}
b, _ := json.Marshal(s)
os.Stdout.Write(b)
{"serverName":"Go \"1.0\" ","serverName2":"\"Go \\\"1.0\\\" \""}

Marshal 函数只有在转换成功的时候才会返回数据,在转换的过程中需要注意几点:

  • json 对象只支持 string 作为 key,所以要编码一个 map,那么必须是 map[string]T 这种类型;
  • channelcomplexfunction 是不能被编码成 json 的;
  • 嵌套的数据是不能编码的,不然会让 json 编码进入死循环;
  • 指针在编码的时候会输出指针指向的内容,而空指针会输出 null;

编码和解码流

json 包提供 DecoderEncoder 类型来支持常用 JSON 数据流读写。NewDecoderNewEncoder 函数分别封装了 io.Readerio.Writer 接口。

func NewEncoder(w io.Writer) *Encoder
func (enc *Encoder) Encode(v interface{}) error

要想把 JSON 直接写入文件,可以使用 json.NewEncoder 初始化文件(或者任何实现 io.Writer 的类型),并调用 Encode();反过来与其对应的是使用 json.NewDecoderDecode() 函数。

func NewDecoder(r io.Reader) *Decoder
func (dec *Decoder) Decode(v interface{}) error
import (
    "encoding/json"
    "fmt"
    "log"
    "os"
)

type Address struct {
    Type    string
    City    string
    Country string
}

type VCard struct {
    FirstName string
    LastName  string
    Addresses []*Address
    Remark    string
}

func main() {
    pa := &Address{"private", "Aartselaar", "Belgium"}
    wa := &Address{"work", "Boom", "Belgium"}
    vc := VCard{"Jan", "Kersschot", []*Address{pa, wa}, "none"}
    // fmt.Printf("%v: \n", vc) // {Jan Kersschot [0x126d2b80 0x126d2be0] none}:
    // JSON format:
    js, _ := json.Marshal(vc)
    fmt.Printf("JSON format: %s", js)
    // using an encoder:
    file, _ := os.OpenFile("vcard.json", os.O_CREATE|os.O_WRONLY, 0666)
    defer file.Close()
    enc := json.NewEncoder(file)
    err := enc.Encode(vc)
    if err != nil {
        log.Println("Error in encoding json")
    }
}