go语言中json操作总结

在 Go 语言中读写 JSON 文件非常常见,通常使用标准库 encoding/json

1. 前提:定义一个结构体(用于序列化/反序列化)
复制代码
type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email"`
}

注意:结构体字段必须首字母大写 (导出),并用 json 标签指定 JSON 中的字段名。

2. 读取 JSON 文件(反序列化)

方法:使用 os.ReadFile + json.Unmarshal

复制代码
package main

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

type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email"`
}

func main() {
    data, err := os.ReadFile("person.json")
    if err != nil {
        panic(err)
    }

    var p Person
    if err := json.Unmarshal(data, &p); err != nil {
        panic(err)
    }

    fmt.Printf("读取到: %+v\n", p)
}

✅ 适用于整个 JSON 对象能放入内存的情况(绝大多数场景)。

3. 一次性写入 JSON 文件(覆盖写入)

方法:使用 json.Marshal + os.WriteFile

复制代码
func main() {
    p := Person{
        Name:  "Alice",
        Age:   30,
        Email: "alice@example.com",
    }

    jsonData, err := json.MarshalIndent(p, "", "  ") // 使用 MarshalIndent 更美观
    if err != nil {
        panic(err)
    }

    err = os.WriteFile("person.json", jsonData, 0644)
    if err != nil {
        panic(err)
    }

    fmt.Println("JSON 文件已写入")
}

🔸 json.MarshalIndent 会格式化输出(带缩进),适合人类阅读;

🔸 若性能优先,可用 json.Marshal(无换行和空格)。

4. 带缓冲写入 JSON(适合流式或多次写入)

场景:比如你要写入一个 JSON 数组,但数据量大,不想一次性加载到内存。

示例:逐个写入多个对象到 JSON 数组(需手动处理格式)

复制代码
package main

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

type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email"`
}

func main() {
    file, err := os.Create("people.json")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    writer := bufio.NewWriter(file)
    defer writer.Flush()

    // 写入数组开头
    writer.WriteString("[\n")

    people := []Person{
        {"Bob", 25, "bob@example.com"},
        {"Carol", 28, "carol@example.com"},
    }

    for i, p := range people {
        jsonData, _ := json.Marshal(p)
        writer.Write(jsonData)
        if i < len(people)-1 {
            writer.WriteString(",\n") // 最后一个不加逗号
        } else {
            writer.WriteString("\n")
        }
    }

    // 写入数组结尾
    writer.WriteString("]\n")
}

⚠️ 注意:这种方式需要手动维护 JSON 格式 (如逗号、括号)。

如果你希望更安全地流式写入 JSON,可考虑使用 json.Encoder

5. jsonl-w(推荐用于流式写入)

json.Encoder 可以直接将 Go 对象编码并写入 io.Writer(如文件),自动处理格式。

示例:写入多个独立 JSON 对象(每行一个,即 JSONL 格式)

复制代码
file, err := os.Create("people.jsonl") // .jsonl 表示每行一个 JSON 对象
if err != nil {
    panic(err)
}
defer file.Close()

encoder := json.NewEncoder(file)

people := []Person{
    {"Dave", 35, "dave@example.com"},
    {"Eve", 32, "eve@example.com"},
}

for _, p := range people {
    if err := encoder.Encode(p); err != nil {
        panic(err)
    }
    // Encode 会自动在每条记录后加换行
}

✅ 优点:

  • 自动处理编码;
  • 每次调用 Encode 写一行有效 JSON;
  • 适合日志、大数据流等场景(称为 JSON Lines / NDJSON 格式)。

❌ 缺点:不能直接生成一个完整的 JSON 数组(除非你额外包装)。

6. jsonl-w 追加
复制代码
package main

import (
	"encoding/json"
	"os"
)

// Person 表示一个人的基本信息
type Person struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Email string `json:"email"`
}

// writePeopleToJSONL 将一组 Person 写入 JSONL 文件(覆盖模式)
func writePeopleToJSONL(filename string, people []Person) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	for _, p := range people {
		if err := encoder.Encode(p); err != nil {
			return err
		}
	}
	return nil
}

// appendPersonToJSONL 向 JSONL 文件追加单个 Person(适合日志式写入)
func appendPersonToJSONL(filename string, p Person) error {
	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return err
	}
	defer file.Close()

	return json.NewEncoder(file).Encode(p)
}

func main() {
	people := []Person{
		{"Dave", 35, "dave@example.com"},
		{"Eve", 32, "eve@example.com"},
	}

	// 覆盖写入初始数据
	if err := writePeopleToJSONL("people.jsonl", people); err != nil {
		panic(err)
	}

	// 示例:后续追加新记录
	newPerson := Person{"Frank", 29, "frank@example.com"}
	if err := appendPersonToJSONL("people.jsonl", newPerson); err != nil {
		panic(err)
	}

	println("✅ JSONL 文件已成功写入和追加")
}
7. jsonl-r

适用于文件不大(比如几百 MB 以内),希望一次加载所有记录到内存。

复制代码
package main

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

type Person struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Email string `json:"email"`
}

// readAllJSONL 一次性读取整个 JSONL 文件为 []Person
func readAllJSONL(filename string) ([]Person, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var people []Person
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		var p Person
		if err := json.Unmarshal(scanner.Bytes(), &p); err != nil {
			return nil, err // 或跳过错误行
		}
		people = append(people, p)
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return people, nil
}

func main() {
	people, err := readAllJSONL("people.jsonl")
	if err != nil {
		panic(err)
	}

	for _, p := range people {
		println(p.Name, "is", p.Age, "years old")
	}
}
8. jsonl-r-b

适用于大文件或需要流式处理(如导入数据库、ETL 等)

复制代码
// JSONLReader 支持批量读取 JSONL
type JSONLReader struct {
	scanner *bufio.Scanner
}

func NewJSONLReader(filename string) (*JSONLReader, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	return &JSONLReader{scanner: bufio.NewScanner(file)}, nil
}

// ReadBatch 读取最多 batchSize 行,返回实际读取的记录
func (r *JSONLReader) ReadBatch(batchSize int) ([]Person, error) {
	var batch []Person
	count := 0

	for r.scanner.Scan() && count < batchSize {
		var p Person
		if err := json.Unmarshal(r.scanner.Bytes(), &p); err != nil {
			// 可选择:跳过错误行 or 返回错误
			continue // 跳过无效行
		}
		batch = append(batch, p)
		count++
	}

	if err := r.scanner.Err(); err != nil {
		return nil, err
	}

	return batch, nil
}

// Close 关闭文件(可选,依赖 GC 也可)
func (r *JSONLReader) Close() error {
	// bufio.Scanner 不提供 Close,但底层 file 已打开
	// 若需精确控制,可将 *os.File 存入结构体
	return nil
}

调用

复制代码
func main() {
	reader, err := NewJSONLReader("people.jsonl")
	if err != nil {
		panic(err)
	}
	defer reader.Close()

	batchSize := 2
	for {
		batch, err := reader.ReadBatch(batchSize)
		if err != nil {
			panic(err)
		}
		if len(batch) == 0 {
			break // 读取完毕
		}

		println("处理一批数据,共", len(batch), "条")
		for _, p := range batch {
			println(" →", p.Name)
		}
	}
}
9. 读取未知 json【Unmarshal】
复制代码
package main

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

func main() {
    data, err := os.ReadFile("unknown.json")
    if err != nil {
        panic(err)
    }

    var result map[string]interface{}
    if err := json.Unmarshal(data, &result); err != nil {
        panic(err)
    }

    // 打印整个结构
    fmt.Printf("解析结果: %+v\n", result)

    // 安全地访问嵌套字段(需类型断言)
    if name, ok := result["name"].(string); ok {
        fmt.Println("Name:", name)
    }

    if addr, ok := result["address"].(map[string]interface{}); ok {
        if city, ok := addr["city"].(string); ok {
            fmt.Println("City:", city)
        }
    }
}

// {"c":{"catalog_list":[{"text":"你好,这是文字"}]}}
// fmt.Printf("解析结果: %+v\n", result["c"].(map[string]interface{})["catalog_list"].([]interface{})[0].(map[string]interface{})["text"])
10. 读取未知 json-【gjson】
复制代码
package main

import (
	"fmt"

	"github.com/tidwall/gjson"
)

func main() {
	// 模拟一个复杂的 JSON 字符串(可以来自 HTTP 响应、文件等)
	jsonStr := `
{
  "name": "Alice",
  "age": 30,
  "active": true,
  "score": 95.7,
  "tags": ["go", "json", "dynamic"],
  "address": {
    "city": "Shanghai",
    "country": "China",
    "coordinates": {
      "lat": 31.2304,
      "lng": 121.4737
    }
  },
  "projects": [
    {"name": "GoTool", "status": "active", "stars": 120},
    {"name": "OldData", "status": "archived", "stars": 5},
    {"name": "NewAPI", "status": "active", "stars": 300}
  ]
}
`

	// 1️⃣ 基本字段获取
	fmt.Println("Name:", gjson.Get(jsonStr, "name").String())
	fmt.Println("Age:", gjson.Get(jsonStr, "age").Int())
	fmt.Println("Active:", gjson.Get(jsonStr, "active").Bool())
	fmt.Println("Score:", gjson.Get(jsonStr, "score").Float())

	// 2️⃣ 嵌套对象
	fmt.Println("City:", gjson.Get(jsonStr, "address.city").String())
	fmt.Println("Latitude:", gjson.Get(jsonStr, "address.coordinates.lat").Float())

	// 3️⃣ 数组访问(索引从 0 开始)
	fmt.Println("First tag:", gjson.Get(jsonStr, "tags.0").String())
	fmt.Println("Last project name:", gjson.Get(jsonStr, "projects.-1.name").String()) // -1 表示最后一个

	// 4️⃣ 获取整个数组或对象
	tags := gjson.Get(jsonStr, "tags")
	fmt.Println("All tags:", tags.Array()) // 返回 []gjson.Result

	// 5️⃣ 遍历数组
	gjson.Get(jsonStr, "projects").ForEach(func(key, value gjson.Result) bool {
		name := value.Get("name").String()
		stars := value.Get("stars").Int()
		fmt.Printf("Project: %s, Stars: %d\n", name, stars)
		return true // 返回 false 可提前终止
	})

	// 6️⃣ 条件查询:找出 status == "active" 的项目
	activeProjects := gjson.Get(jsonStr, `projects.#(status=="active")`)
	fmt.Println("\nActive projects (raw):", activeProjects.String())

	// 7️⃣ 获取符合条件的项目的名称列表
	activeNames := gjson.Get(jsonStr, `projects.#(status=="active")#.name`)
	fmt.Print("Active project names: ")
	activeNames.ForEach(func(_, name gjson.Result) bool {
		fmt.Print(name.String(), " ")
		return true
	})
	fmt.Println()

	// 8️⃣ 数值比较:stars > 100
	popular := gjson.Get(jsonStr, `projects.#(stars>100)#.name`)
	fmt.Print("Popular projects (stars > 100): ")
	popular.ForEach(func(_, name gjson.Result) bool {
		fmt.Print(name.String(), " ")
		return true
	})
	fmt.Println()

	// 9️⃣ 安全取值:如果字段不存在,返回默认值
	email := gjson.Get(jsonStr, "email").String()
	if !gjson.Get(jsonStr, "email").Exists() {
		email = "no-email@example.com"
	}
	fmt.Println("Email:", email)

	// 🔟 使用 GetMany 一次性取多个字段(性能更高)
	results := gjson.GetMany(jsonStr, "name", "address.city", "age")
	name, city, age := results[0], results[1], results[2]
	fmt.Printf("\nSummary: %s, %s, %d years old\n", name.String(), city.String(), age.Int())
}
11. json 添加未知 dict 或者未知 list
复制代码
package main

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

func main() {
	// 主动态对象
	data := make(map[string]any)
	data["name"] = "Alice"

	// 模拟一个深层嵌套的 HTTP 响应 JSON(实际中来自 http.Get)
	// 这里用字符串模拟,你可以替换成真实请求
	fakeHTTPResponseBody := `{
		"street": "123 Main St",
		"city": "Shanghai",
		"geo": {
			"lat": 31.2304,
			"lng": 121.4737,
			"details": {
				"accuracy": "high",
				"source": ["GPS", "MapAPI"]
			}
		},
		"history": [
			{"year": 2020, "event": "moved in"},
			{"year": 2023, "event": "renovated"}
		]
	}`

	// 步骤 1:解析 HTTP 返回的 JSON 到 any(自动处理任意嵌套)
	var addressData any
	if err := json.Unmarshal([]byte(fakeHTTPResponseBody), &addressData); err != nil {
		log.Fatal("Failed to parse address JSON:", err)
	}

	// 步骤 2:直接赋值给 data["address"]
	data["address"] = addressData

	fakeHTTPResponseListBody:=`[
		{"id": 1, "name": "Alice","address":{
			"street": "123 Main St",
			"city": "Shanghai"
		}},
		{"id": 2, "name": "Bob","address":{
			"street": "456 Elm St",
			"city": "Beijing"
		}}
	]`
	var listData []map[string]any
	if err := json.Unmarshal([]byte(fakeHTTPResponseListBody), &listData); err != nil {
		log.Fatal("Failed to parse list JSON:", err)
	}
	data["list"] = listData
	

	// 验证:打印整个结构
	output, _ := json.MarshalIndent(data, "", "  ")
	fmt.Println(string(output))
}
相关推荐
王中阳Go背后的男人9 小时前
GoFrame vs Laravel:从ORM到CLI工具的全面对比与迁移指南
后端·go
阿达_优阅达10 小时前
Tableau 2025.3 发布!可视化扩展升级、Server 版 Agent、平台数据 API,让 AI 深度融入业务工作流
人工智能·ai·数据分析·数据可视化·仪表板·tableau·版本更新
叮铃铃上课了10 小时前
HiveSQL 中的集合运算
数据分析
卡尔特斯10 小时前
Go 安装插件更换国内镜像
go
王中阳Go10 小时前
都2026年了,PHP还纠结转Go还是Java呢?安利一个无缝迁移的框架~
java·后端·go
卡尔特斯11 小时前
go get 快速入门(自用笔记)
go
青鱼入云11 小时前
@JsonValue和@JsonCreator介绍
json·jackson
计算机徐师兄12 小时前
Python基于Django的MOOC线上课程推荐数据分析与可视化系统(附源码,文档说明)
python·数据分析·django·慕课线上课程推荐·慕课线上课程推荐可视化系统·pytho线上课程推荐可视化·线上课程推荐数据分析可视化系统
得物技术13 小时前
Go语言在高并发高可用系统中的实践与解决方案|得物技术
go