Golang json 解析与生成

时间:2025-03-08 07:04:58

文章目录

  • 1.解析 JSON
    • 1.1 map[string]any 存储 json
    • 1.2 struct 存储 json
    • 1.3 []map[string]any 解析 json 数组
  • 2.生成 JSON
    • 2.1 struct 序列化为 json
    • 2.2 map[string]any 序列化为 json
    • 2.3 一个较为复杂的例子
      • 2.3.1 使用 struct + slice
      • 2.3.2 使用 map[string]any + []any
  • 参考文献

JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于阅读。尽管 JSON 是 JavaScript 的一个子集,但 JSON 是独立于语言的文本格式,并且采用了类似于 C 语言家族的一些习惯。JSON 与 XML 最大的不同在于 XML 是一个完整的标记语言,而 JSON 不是。JSON由于比 XML 更小、更快,更易解析,以及浏览器的內建支持,使得其更适用于网络数据传输领域。

Golang 自带的 JSON 解析库 encoding/json,可以用来将结构化数据序列化成 json 字符串或从 json 字符串中解析出我们想要的数据。

1.解析 JSON

1.1 map[string]any 存储 json

给一个较为复杂的 json 字符串,包含数组,数组的元素是 json 对象。我们需要取出数组第一个元素中的某一个字段值。其它的解析均可参考下面的代码。

package main

import (
   "encoding/json"
   "fmt"
)

func main() {
        jsonStr := []byte(`{"uin":1589276509,"feedID":10000,"videos":[{"picture":"/","duration":"839"}]}`)
        var jsonMap map[string]any
        if err := json.Unmarshal(jsonStr, &jsonMap); err!=nil {
           fmt.Printf("json decode failed, err=%v", err)
           return
        }
        if  value, ok := jsonMap["videos"]; ok {
        	fmt.Printf("value=%#v\n", value)
     		if sliceValue, ok := value.([]iany); ok {
				if mapValue, ok := sliceValue[0].(map[string]any); ok {
					if duration, ok := mapValue["duration"]; ok {
						fmt.Printf("d=%v,type=%T\n", duration, duration)
					}
				}
			}
		}
}

程序输出:

value=[]interface {}{map[string]interface {}{"picture":"/", "duration":"839"}}
d=839,type=string

解析 json 字符串时,需要注意如下几点:
(1)Golang 类型和 JSON 类型的对应关系如下:

map[string]any 代表 JSON 对象
[]any 代表 JSON 数组
bool 代表 JSON boolean
float64 代表 JSON number
string 代表 JSON string
nil 代表 JSON null

1.2 struct 存储 json

如果我们事先知道 JSON 串,那么可以指定具体的 struct 来存储解析后的 json。可以通过在线工具Golang: Convert JSON to Struct快速将 JOSN 转为 Go struct。

package main

import (
	"fmt"
	"encoding/json"
)

type FeedsItem struct {
	Title             string   `json:"string_title"`
	Score             int      `json:"uint32_score"`
	AiKongbuLevel     int      `json:"uint32_ai_kongbu_level"`
	AiDisuLevel       int      `json:"uint32_ai_disu_level"`
	AiBiaotidangLevel int      `json:"uint32_ai_biaotidang_level"`
	AiUnsocialLevel   int      `json:"uint32_ai_unsocial_level"`
}

func main() {
    data:=`[{
		"uint32_feed_pos": 1,
		"string_title": "它才是“天然补血王”,女性隔三差五吃,美容驻颜,气色更红润",
		"uint32_score": 4,
		"uint32_ai_kongbu_level": 0,
		"uint32_ai_disu_level": 0,
		"uint32_ai_biaotidang_level": 1,
		"uint32_ai_unsocial_level": 0
		}, {
		"uint32_feed_pos": 2,
		"string_title": "7岁的火星男孩,顺利通过测谎仪,现在为何消失不见了?",
		"uint32_score": 4,
		"uint32_ai_kongbu_level": 0,
		"uint32_ai_disu_level": 0,
		"uint32_ai_biaotidang_level": 0,
		"uint32_ai_unsocial_level": 0
	}]`

	var feedsInfo []FeedsItem
	//第二个参数必须是指针,否则无法接收解析后的数据
   	if err := json.Unmarshal([]byte(data), &feedsInfo); err != nil {
		fmt.Printf("() failed, err=%v data=%s\n", err, data)
		return
   	}
	fmt.Printf("jsonMap=%#v\n", feedsInfo)	
}

编译运行输出:

jsonMap=[]{{Title:"它才是“天然补血王”,女性隔三差五吃,美容驻颜,气色更红润", Score:4, AiKongbuLevel:0, AiDisuLevel:0, AiBiaotidangLevel:1, AiUnsocialLevel:0}, {Title:"7岁的火星男孩,顺利通过测谎仪,现在为何消失不见了?", Score:4, AiKongbuLevel:0, AiDisuLevel:0, AiBiaotidangLevel:0, AiUnsocialLevel:0}}

1.3 []map[string]any 解析 json 数组

如果 json 字符串是一个 json 数组,除了可以使用 struct 来解析,也可以使用 []map[string]any来解析。

package main

import (
        "fmt"
        "encoding/json"
)

func main() {
	s := `[{"id":300001,"exp_layer":"string","buckets":"1,2,3"},{"id":300002,"exp_layer":"test2","buckets":"4"}]`

	var jsonArray []map[string]any
	if err := json.Unmarshal([]byte(s), &jsonArray); err!=nil {
		fmt.Printf("json decode failed, err=%v value=%v\n", err, s)
			return
	}   
	fmt.Printf("jsonArray=%+v\n", jsonArray)
}

输出结果:

jsonArray=[map[exp_layer:string buckets:1,2,3 id:300001] map[id:300002 exp_layer:test2 buckets:4]]

2.生成 JSON

解析 json 时,可以使用 map[string]any 或者 struct 来存储解析后的数据。同样地,我们可以将任意 Golang 对象序列化为 JSON。

Golang 中,JSON 对象表示主要有两种方式,一种是 struct,另一种是 map[string]any。在表示数组时,分别使用具体类型的切片和任意类型的切片 []any。

2.1 struct 序列化为 json

假设我们有如下一个结构体 Student 及其一个实例对象 st,将其序列化为 json,具体实现如下:

package main

import (
   "encoding/json"
   "fmt"
)

type Student struct {
    Name 		string `json:"name"`
    Age   		int
    gender   	string
    Class 		*Class `json:"class"`
}

type Class struct {
    Name  string
    Grade int
}

func main() {
	//实例化一个数据结构,用于生成 json 字符串
    st := Student{
        Name: "张三",
        Age:  18,
        gender:  "男",
    }

    //指针变量
    cla := new(Class)
    cla.Name = "1班"
    cla.Grade = 3
    st.Class=cla

    //Marshal失败时 err != nil
    jsonStu, err := json.Marshal(st)
    if err != nil {
        fmt.Println("生成json字符串错误")
    }

    //jsonStu是[]byte类型,转化成string类型便于查看
    fmt.Println(string(jsonStu))
}

程序输出结果:

{"name":"张三","Age":18,"class":{"Name":"1班","Grade":3}}

阅读以上代码可以看出:
(1)只要是可导出成员(变量首字母大写),都可以转成 json。因成员变量 gender 是不可导出的,故无法转成 json;
(2)如果变量打上了 json 标签,如 Name 旁边的 json:"name" ,那么转化成的 json key 就用该标签 “name”,否则取字段名作为 key,如 “Age”;
(3)指针变量,编码时自动转换为它所指向的值,如 Class 变量;
(4)强调一句,序列化成功后的 json 字符串是纯粹的字符串。

2.2 map[string]any 序列化为 json

package main

import (
   "encoding/json"
   "fmt"
)

func main() {
	m := make(map[string]any)
	m["Name"]="张三"
	m["Age"]=18
	m["Gender"]="男"
	
	jsonObject := make(map[string]any)
	jsonObject["Name"]="1班"
	jsonObject["Grade"]=3
	
	m["Class"]=jsonObject
	
	//将 map[string]any 转换为 json
    sJson, _ := json.Marshal(m)
    fmt.Printf("sJson=%v\n", string(sJson))
}

编译运行输出:

sJson={"Age":18,"Class":{"Grade":3,"Name":"1班"},"Gender":"男","Name":"张三"}

2.3 一个较为复杂的例子

假如需要生成如下一个较为复杂的 json:

{
	"requests": [{
		"positions": [{
			"ads": [{
				"bid_result": "1",
				"ecpm": "1.1",
				"view_id": "Y940raTWuc2bAbPtq1lEc"
			}, {
				"bid_result": "2",
				"ecpm": "2.2",
				"view_id": "L37Qw!KwBORErDPbViQ"
			}]
		}]
	}]
}

其中,requests 为一个数组,数组的元素是一个 json 对象,该对象只有一个成员 positions;positions 也是一个数组,数组的元素也是一个 json 对象,该对象也只有一个成员 ads;ads 同样还是数组,数组元素还是一个 json 对象。

2.3.1 使用 struct + slice

因为在 Golang 中,一个 json 对象可以使用 struct 表示,json 中的数组可以使用 slice 来表示,于是我们可以使用 struct 和 slice 来表示上面的 json。

package main

import (
   "encoding/json"
   "fmt"
)

type Request struct{
	Requests []RequestValue	`json:"requests"`
}

type RequestValue struct {
	Positions []PositionValue `json:"positions"`
}

type PositionValue struct {
	Ads []AdsValue `json:"ads"`
}

type AdsValue struct {
	Bid_result string       `json:"bid_result"`    
	Ecpm string             `json:"ecpm"`
	View_id string          `json:"view_id"`
}

func main() {
	positionValue := PositionValue{
		Ads: []AdsValue{
			AdsValue{
				Bid_result:"1",
				Ecpm:"1.1",
				View_id:"Y940raTWuc2bAbPtq1lEc",
			},
			AdsValue{
				Bid_result:"2",
				Ecpm:"1.2",
				View_id:"L37Qw!KwBORErDPbViQ",
			},
		},
	}
	
	requestValue := RequestValue{
		Positions: []PositionValue{positionValue},
	}
	
	request := Request{
		Requests: []RequestValue{requestValue}
	}
	
	//将 struct 转换为 json
    sJson, _ := json.Marshal(request)
    fmt.Printf("sJson=%v\n", string(sJson))
}

编译运行输出:

sJson={"requests":[{"positions":[{"ads":[{"bid_result":"1","ecpm":"1.1","view_id":"Y940raTWuc2bAbPtq1lEc"},{"bid_result":"2","ecpm":"1.2","view_id":"L37Qw!KwBORErDPbViQ"}]}]}]}

2.3.2 使用 map[string]any + []any

同样地,可以使用 map[string]any 来表示任意的 json,如果元素是数组,可以使用 []any。

package main

import (
   "encoding/json"
   "fmt"
)

func main() {
        //json 对象    
        adValue := make(map[string]any)
        adValue["bid_result"]="1"
        adValue["ecpm"]="1.1"
        adValue["view_id"]="Y940raTWuc2bAbPtq1lEc"

        adValue1 := make(map[string]any)
        adValue1["bid_result"]="2"
        adValue1["ecpm"]="1.2"
        adValue1["view_id"]="L37Qw!KwBORErDPbViQ"

        //json 对象,只有一个成员 ads,且 ads 是一个 json 对象数组
        ads := make(map[string]any)
        ads["ads"]=[]any{adValue, adValue1}

        //json 对象,只有一个成员 positions,且 positions 是一个 json 对象数组
        positions := make(map[string]any)
        positions["positions"]=[]any{ads}
    
        //json 对象,只有一个成员 requests,且 requests 是一个 json 对象数组
        requests := make(map[string]any)
        requests["requests"] = []any{positions}
    
        //将 map[string]any 转换为 json
        sJson, _ := json.Marshal(requests)
        fmt.Printf("sJson=%v\n", string(sJson))
}

编译运行输出:

sJson={"requests":[{"positions":[{"ads":[{"bid_result":"1","ecpm":"1.1","view_id":"Y940raTWuc2bAbPtq1lEc"},{"bid_result":"2","ecpm":"1.2","view_id":"L37Qw!KwBORErDPbViQ"}]}]}]}

输出结果与使用 struct 相同。相比于 struct,使用 map[string]any 更加的简洁,因为省去了 struct 的定义。


参考文献

Go的json解析:Marshal与Unmarshal
: json: cannot unmarshal array into Go value of type
Introducing JSON -