在 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))
}