json 解析:marshal 和 unmarshal


Go 使用 encoding/json 包的 marshal 和 unmarshal 实现 json 数据的编解码。分别记录如下:

1. marshal

定义结构体:

type OCP struct {
	Name          string         `json:"name"`
	ImageRegistry *ImageRegistry `json:"imageRegistry"`
	Status        string         `json:"status"`
	Events        []string       `json:"events"`
	id            *int
}

type ImageRegistry struct {
	Addr     string `json:"addr"`
	User     string `json:"user"`
	Password string `json:"password"`
}

将结构体 OCP 类型值编码成 json 格式:

func main() {
	imageRegistry := ImageRegistry{
		Addr:     "x.x.x.x",
		User:     "admin",
		Password: "admin",
	}

	ocp := OCP{
		Name:          "lubanseven",
		ImageRegistry: &imageRegistry,
		Status:        "running",
		Events:        []string{"normal", "normal", "normal"},
	}

	data, err := json.Marshal(ocp)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(string(data))
}

输出:

{"name":"lubanseven","imageRegistry":{"addr":"x.x.x.x","user":"admin","password":"admin"},"status":"running","events":["normal","normal","normal"]}

从输出可以看出:

  1. 结构体中以小写字母开头的类型(id)不会被编码。
  2. 结构体中指针类型在编码时将转换为指针所指向的值。

更多编码规则可看 JSON and Go

2. unmarshal

类似的将 data 解码为结构体类型 OCP 的值:

var lubanseven OCP
err = json.Unmarshal(data, &lubanseven)
if err != nil {
	fmt.Println(err)
}
fmt.Println(lubanseven)

输出:

{lubanseven 0xc00020e360 running [normal normal normal] <nil>}

从输出可以看出:

  1. 指针类型的值在解码时将转换为值的地址。
  2. 对于无法解码的值,将转换为该值的零值,如指针类型变量 id 解码为 nil。

更多编码规则可看 JSON and Go

对于第二点,将 id 类型定义成 int,查看解码后的结构体值:

type OCP struct {
	Name          string         `json:"name"`
	ImageRegistry *ImageRegistry `json:"imageRegistry"`
	Status        string         `json:"status"`
	Events        []string       `json:"events"`
	id            int
}

输出:

{lubanseven 0xc0000cc4b0 running [normal normal normal] 0}

这点在解码的时候要注意。

上例中解码的结构体类型是已知的,如果结构体未知该如何确定结构体类型呢?这在实际场景中是会发生的,比如传输的数据流,事先不知道其结构体类型。
可以通过类型断言的方式,确定结构体类型,如下:

var lubanunknown interface{}
err = json.Unmarshal(data, &lubanunknown)
if err != nil {
	fmt.Println(err)
}
fmt.Println(lubanunknown)

if value, ok := lubanunknown.(map[string]interface{}); ok {
	fmt.Println(value)
}

输出:

map[events:[normal normal normal] imageRegistry:map[addr:x.x.x.x password:admin user:admin] name:lubanseven status:running]
map[events:[normal normal normal] imageRegistry:map[addr:x.x.x.x password:admin user:admin] name:lubanseven status:running]

输出类型是 map[string]interface{},key 是 string,value 是 interface{},可以对 value 进行类型断言确定 interface{} 的实质类型(直接看输出也能看出来...):

value := lubanunknown.(map[string]interface{})

for k, v := range value {
	switch vv := v.(type) {
	case int:
		fmt.Println(k, "is int: ", vv)
	case string:
		fmt.Println(k, "is string: ", vv)
	case []interface{}:
		fmt.Println(k, "is slice: ", vv)
		for _, v := range vv {
			fmt.Println(v)
		}
	case map[string]interface{}:
		fmt.Println(k, "is struct: ", vv)
	default:
		fmt.Println(k, "unexpect type of ", reflect.TypeOf(vv))
	}
}

输出:

imageRegistry is struct:  map[addr:x.x.x.x password:admin user:admin]
status is string:  running
events is slice:  [normal normal normal]
normal
normal
normal
name is string:  lubanseven

3. 数据流的 json 编解码

对数据流的编解码,如下:

package main

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

func main() {
    dec := json.NewDecoder(os.Stdin)
    enc := json.NewEncoder(os.Stdout)
    for {
        var v map[string]interface{}
        if err := dec.Decode(&v); err != nil {
            log.Println(err)
            return
        }
        for k := range v {
            if k != "Name" {
                delete(v, k)
            }
        }
        if err := enc.Encode(&v); err != nil {
            log.Println(err)
        }
    }
}

示例来自 JSON and Go

运行示例:

$ go run main.go 
{"name":"lubanseven","imageRegistry":{"addr":"x.x.x.x","user":"admin","password":"admin"},"status":"running","events":["normal","normal","normal"]}
{}
{"Name":"lubanseven","imageRegistry":{"addr":"x.x.x.x","user":"admin","password":"admin"},"status":"running","events":["normal","normal","normal"]}
{"Name":"lubanseven"}

posted @ 2022-02-16 23:16  lubanseven  阅读(492)  评论(0编辑  收藏  举报