Go进阶-JSON和YAML的操作

TOC

一、JSON数据操作

Go语言标准库已经内置了encoding/json库,不需要额外安装。

JSON方法大全

写入JSON数据

需要配置os的文件操作方法进行

file, _ := os.Create("cats.json")
err := json.NewEncoder(file).Encode()
// 还可以在写入的时候进行格式优化
encoder := json.NewEncoder(file)
// 设置漂亮格式(缩进)并写入data数据到文件
encoder.SetIndent("", "  ").Encode(data)

读取json数据

需要配置os的文件操作方法进行

file, _ := os.Open("cats.json")
// 读取文件数据传入到data指针
err := json.NewDecoder(file).Decode(&data)

编码和解码

编码Go对象成JSON数据和解码JSON数据成Go对象

// 编码Go对象成JSON数据
data := map[string]interface{}{"name": "miaomi", "quantity": 123, "breed": "ragdoll"}
jsonBytes, _ := json.Marshal(data)
fmt.Println(string(jsonBytes))
/* 运行结果如下
{"breed":"ragdoll","name":"miaomi","quantity":123}
*/
// 解码JSON数据成Go对象
json_data := `{"name": "miaomi", "quantity": 123, "breed": "ragdoll"}`
var result map[string]interface{}
_ = json.Unmarshal([]byte(json_data), &result)
fmt.Println(result)
/* 运行结果如下
map[breed:ragdoll name:miaomi quantity:123]
*/

美化JSON格式

data := map[string]interface{}{"name": "miaomi", "quantity": 123, "breed": "ragdoll"}
jsonBytes, _ := json.MarshalIndent(data, "", "  ")
fmt.Println(string(jsonBytes))
/* 运行结果如下
{
  "breed": "ragdoll",
  "name": "miaomi",
  "quantity": 123
}
*/

读取json文件

示例都通过该JSON文件内容为基础。json文件cats.json文件内容如下:

{
  "name": "miaomi",
  "quantity": 123,
  "variety": [
    "sphynx",
    "ragdoll",
    "persian",
    "siamese"
  ],
  "prices": {
    "sphynx": 800,
    "ragdoll": 1300,
    "persian": 4700,
    "siamese": 2300
  }
}

从JSON 文件读取嵌套结构体

一般在知道json数据返回格式后,才方便使用结构体,灵活性太差。
【示例】代码如下所示:

package main

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

type Prices struct {
	Sphynx  int `json:"sphynx"`
	Ragdoll int `json:"ragdoll"`
	Persian int `json:"persian"`
	Siamese int `json:"siamese"`
}

type Shop struct {
	Name       string   `json:"name"`
	Quantity   int      `json:"quantity"`
	Variety    []string `json:"variety"`
	Price_list Prices   `json:"prices"`
}

func main() {
	file, err := os.Open("cats.json")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	// 定义data为结构体Shop数据类型格式
	var data Shop
	decoder := json.NewDecoder(file)
	// 读取文件里面的JSON数据并赋值给data指针
	err = decoder.Decode(&data)
	if err != nil {
		panic(err)
	}
	// 打印结构体里的变量值
	fmt.Println("Ragdoll品种的价格:", data.Price_list.Ragdoll)
	fmt.Println("猫的种类:", data.Variety)
}
/* 运行结果如下
Ragdoll品种的价格: 1300
猫的种类: [sphynx ragdoll persian siamese]
*/

从JSON 文件读取map数据

【示例】代码如下所示:

package main

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

func main() {
	file, err := os.Open("cats.json")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	// 定义data为map数据类型格式
	var data map[string]interface{}
    // 读取文件里面的JSON数据并赋值给data指针
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&data)
	if err != nil {
		panic(err)
	}
	fmt.Println("整体数据结构:", data)
	fmt.Println("prices里的数据", data["prices"])
	/* 获取prices里面persian品种的价格
    定义的map后面数据是interface{}类型,所以需要转化一个数据类型 */
	a := data["prices"].(map[string]interface{})["persian"].(float64)
	fmt.Println("persian品种的价格:", a)
}
/* 运行结果如下
整体数据结构: map[name:miaomi prices:map[persian:4700 ragdoll:1300 siamese:2300 sphynx:800] quantity:123 variety:[sphynx ragdoll persian siamese]]
prices里的数据 map[persian:4700 ragdoll:1300 siamese:2300 sphynx:800]
persian品种的价格: 4700
*/

写入json数据

写入嵌套结构体到 JSON 文件

【示例】代码如下所示:

package main

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

type Prices struct {
	Sphynx  int `json:"sphynx"`
	Ragdoll int `json:"ragdoll"`
	Persian int `json:"persian"`
	Siamese int `json:"siamese"`
}

type Shop struct {
	Name       string   `json:"name"`
	Quantity   int      `json:"quantity"`
	Variety    []string `json:"variety"`
	Price_list Prices   `json:"prices"`
}

func main() {
    // 初始化结构体的值
	shop := Shop{
		Name:     "miaomi",
		Quantity: 123,
		Variety:  []string{"sphynx", "ragdoll", "persian", "siamese"},
		Price_list: Prices{
			Sphynx:  800,
			Ragdoll: 1300,
			Persian: 4700,
			Siamese: 2300,
		},
	}
    // 打开创建JSON文件: cats.json
	file, err := os.Create("cats.json")
	if err != nil {
		panic(err)
	}
	defer file.Close()
    // 写入结构体数据到JSON文件中
	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ") // 设置漂亮格式(缩进)
	err = encoder.Encode(shop)
	if err != nil {
		panic(err)
	}
}

通过map数据写入到JSON文件

【示例】代码如下所示:

package main

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

func main() {
	// 初始化赋值变量data,数据类型为map
	data := map[string]interface{}{
		"name":     "miaomi",
		"quantity": 123,
		"prices": map[string]float64{
			"sphynx":  800,
			"ragdoll": 1300,
			"persian": 4700,
			"siamese": 2300,
		},
		"variety": []string{
			"sphynx",
			"ragdoll",
			"persian",
			"siamese",
		},
	}
	// 打开创建JSON文件: cats.json
	file, err := os.Create("cats2.json")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	// 写入结构体数据到JSON文件中
	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ") // 设置漂亮格式(缩进)
	err = encoder.Encode(data)
	if err != nil {
		panic(err)
	}
}

二、YAML数据操作

三方库( gopkg.in/yaml.v3)

它支持将YAML文件解析为结构体、从结构体生成 YAML、直接处理 map 或切片等。
安装三方库:gopkg.in/yaml.v3

go get gopkg.in/yaml.v3

go.mod文件示例:

module myapp

go 1.22

require gopkg.in/yaml.v3 v3.0.1

导入库,如下:

import "gopkg.in/yaml.v3"

YAML方法大全

核心函数(Marshal / Unmarshal)

Marshal函数:将结构体(struct)或映射(map)解析成YAML

data := map[string]interface{}{"name": "miaomi", "quantity": 123, "breed": "ragdoll"}
yaml_data, err := yaml.Marshal(data)

Unmarshal函数:将YAML解析成结构体(struct)或映射(map)

var cfg Config
err := yaml.Unmarshal(yaml_data, &cfg)

流式读写(Decoder / Encoder)

流式读取(Decoder):

file, _ := os.Open("cats.json")
// 读取文件数据传入到data指针
err := yaml.NewDecoder(file).Decode(&data)

流式写入(Encoder):

// 初始化赋值data变量
var data Config
file, _ := os.Create("cats.json")
// 写入data数据到文件中
encoder, err := yaml.NewEncoder(file)
// 美化格式
encoder.SetIndent(2)
encoder.Encode(data)

读取yaml文件数据

示例config.yaml文件:

server:
    port: 8080
    host: localhost
database:
    user: admin
    password: secret

将YAML解析成结构体读取数据

【示例】代码如下所示:

package main

import (
	"fmt"
	"os"

	"gopkg.in/yaml.v3"
)
/* 创建结构体,将内部结构体拆分出来
以Config为主结构体,Server和Database为子结构体
*/
type Server struct {
    Port int    `yaml:"port"`
    Host string `yaml:"host"`
}

type Database struct {
    User     string `yaml:"user"`
    Password string `yaml:"password"`
}  

type Config struct {
    Server   Server   `yaml:"server"`
    Database Database `yaml:"database"`
}

func main(){
    // 读取yaml文件内容
	file, err := os.ReadFile("config.yaml")
	if err != nil {
		fmt.Println("读取文件失败:", err)
	}
	// 将读取的文件对象内容解析到结构体中,再从结构体中获取变量值
	var config Config
	err = yaml.Unmarshal(file, &config)
	if err != nil {
		fmt.Println("解析 YAML 失败:", err)
	}
	// 输出结构体值
	fmt.Printf("%+v\n", config)
}
/* 运行结果如下
{Server:{Port:8080 Host:localhost} Database:{User:admin Password:secret}}
*/

将YAML解析成map数据

【示例】代码如下所示:

package main

import (
	"fmt"
	"os"

	"gopkg.in/yaml.v3"
)

func main(){
    // 读取yaml文件内容
	file, err := os.ReadFile("config.yaml")
	if err != nil {
		fmt.Println("读取文件失败:", err)
	}
	// 将读取的文件对象内容解析到map中,再从map中进行处理
	var data map[string]interface{}
	err = yaml.Unmarshal(file, &data)
	if err != nil {
		fmt.Println("解析 YAML 失败:", err)
	}
	fmt.Println(data)
}
/* 运行结果如下
map[database:map[password:secret user:admin] server:map[host:localhost port:8080]]
*/

写入yaml文件数据

注意:
1.Go map 是无序的,所以生成的 YAML 字段顺序可能和你写的 map 顺序不一样。
2.如果需要固定顺序,可以用结构体代替map,或者用yaml.Node手动控制。

将结构体数据写入YAML文件

【示例】代码如下所示:

package main

import (
	"fmt"
	"os"

	"gopkg.in/yaml.v3"
)

/* 创建结构体,将内部结构体拆分出来
以Config为主结构体,Server和Database为子结构体
*/
type Server struct {
    Port int    `yaml:"port"`
    Host string `yaml:"host"`
}

type Database struct {
    User     string `yaml:"user"`
    Password string `yaml:"password"`
}

type Config struct {
    Server   Server   `yaml:"server"`
    Database Database `yaml:"database"`
}

func main(){
    // 初始化赋值Config结构体
	config := Config{
		Server: Server{
			Port: 8080,
			Host: "localhost",
		},
		Database: Database{
			User:     "admin",
			Password: "secret",
		},
	}
	// 将结构体解析成yaml数据
	data, err := yaml.Marshal(&config)
	if err != nil {
		fmt.Println("转换为 YAML 失败:", err)
	}
	// 将解析的yaml数据写入到文件中
	err = os.WriteFile("struct_to_yaml.yaml", data, 0644)
	if err != nil {
		fmt.Println("写入文件失败:", err)
	}
}

写入的struct_to_yaml.yaml文件内容如下:

server:
    port: 8080
    host: localhost
database:
    user: admin
    password: secret

将map数据写入YAML文件

【示例】代码如下所示:

package main

import (
	"fmt"
	"os"

	"gopkg.in/yaml.v3"
)

func main(){
    // 初始化map对象并赋值结构
	mapdata := map[string]interface{}{"name": "Jimmy", "age": 23,
		"spec": map[string]interface{}{
			"server":   map[string]interface{}{"host": "127.0.0.1", "port": 8080},
			"database": map[string]interface{}{"user": "root", "password": "e3uh339j5ih82d"},
		},
		"white_list": []string{"192.168.1.0/24", "172.16.10.187", "10.8.0.0/16"},
	}
	// 将map结构和数据解析成yaml对象
	data, err := yaml.Marshal(&mapdata)
	if err != nil {
		fmt.Println("转换为 YAML 失败:", err)
	}
	// 将解析的yaml数据写入到文件中
	err = os.WriteFile("map_to_yaml.yaml", data, 0644)
	if err != nil {
		fmt.Println("写入文件失败:", err)
	}
}

写入的map_to_yaml.yaml文件内容如下:

age: 23
name: Jimmy
spec:
    database:
        password: e3uh339j5ih82d
        user: root
    server:
        host: 127.0.0.1
        port: 8080
white_list:
    - 192.168.1.0/24
    - 172.16.10.187
    - 10.8.0.0/16