Golang JSON 标准库用法详解

发布于:2025-06-29 ⋅ 阅读:(16) ⋅ 点赞:(0)

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,Go语言的标准库encoding/json提供了强大的JSON处理能力。下面我将详细介绍各种用法并提供示例代码。

1. 基本编码(Marshal)

将Go数据结构转换为JSON字符串。

package main

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

type Person struct {
	Name    string `json:"name"`          // 字段标签指定JSON键名
	Age     int    `json:"age"`           // 基本类型字段
	Address string `json:"address"`       // 字符串字段
	Hobbies []string `json:"hobbies"`     // 切片字段
	IsAdmin bool   `json:"is_admin"`      // 布尔字段
	private string // 小写开头字段不会被导出
}

func main() {
	// 创建一个Person实例
	p := Person{
		Name:    "Alice",
		Age:     30,
		Address: "123 Main St",
		Hobbies: []string{"reading", "hiking"},
		IsAdmin: true,
		private: "secret", // 这个字段不会被编码
	}

	// 将结构体编码为JSON
	jsonData, err := json.Marshal(p)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(jsonData))
	// 输出: {"name":"Alice","age":30,"address":"123 Main St","hobbies":["reading","hiking"],"is_admin":true}
}

2. 美化输出(Indent)

使用MarshalIndent可以生成格式化的JSON输出。

func main() {
	p := Person{
		Name:    "Bob",
		Age:     25,
		Address: "456 Oak Ave",
		Hobbies: []string{"gaming", "coding"},
	}

	// 使用MarshalIndent美化输出
	jsonData, err := json.MarshalIndent(p, "", "  ")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(jsonData))
	/*
	输出:
	{
	  "name": "Bob",
	  "age": 25,
	  "address": "456 Oak Ave",
	  "hobbies": [
	    "gaming",
	    "coding"
	  ],
	  "is_admin": false
	}
	*/
}

3. 基本解码(Unmarshal)

将JSON字符串解码为Go数据结构。

func main() {
	// JSON字符串
	jsonStr := `{
		"name": "Charlie",
		"age": 35,
		"address": "789 Pine Rd",
		"hobbies": ["swimming", "photography"],
		"is_admin": true
	}`

	// 解码JSON到Person结构体
	var p Person
	err := json.Unmarshal([]byte(jsonStr), &p)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%+v\n", p)
	// 输出: {Name:Charlie Age:35 Address:789 Pine Rd Hobbies:[swimming photography] IsAdmin:true private:}
}

4. 处理未知结构的JSON

使用map[string]interface{}interface{}处理未知结构的JSON。

func main() {
	// 复杂的JSON数据
	jsonStr := `{
		"name": "Dave",
		"age": 40,
		"metadata": {
			"department": "IT",
			"role": "manager",
			"permissions": ["read", "write", "delete"]
		}
	}`

	// 解码到空接口
	var data interface{}
	err := json.Unmarshal([]byte(jsonStr), &data)
	if err != nil {
		log.Fatal(err)
	}

	// 类型断言访问数据
	m := data.(map[string]interface{})
	fmt.Println("Name:", m["name"])
	fmt.Println("Age:", m["age"])

	// 访问嵌套数据
	metadata := m["metadata"].(map[string]interface{})
	fmt.Println("Department:", metadata["department"])
	fmt.Println("Permissions:", metadata["permissions"])
}

5. 流式处理(Encoder/Decoder)

对于大文件或网络流,可以使用流式处理。

编码器示例(Encoder)

package main

import (
	"encoding/json"
	"os"
)

func main() {
	type Book struct {
		Title  string `json:"title"`
		Author string `json:"author"`
		Year   int    `json:"year"`
	}

	books := []Book{
		{"The Go Programming Language", "Alan A. A. Donovan", 2015},
		{"Effective Go", "The Go Authors", 2009},
	}

	// 创建文件
	file, err := os.Create("books.json")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	// 创建JSON编码器
	encoder := json.NewEncoder(file)

	// 设置缩进(可选)
	encoder.SetIndent("", "  ")

	// 编码数据到文件
	err = encoder.Encode(books)
	if err != nil {
		panic(err)
	}
}

解码器示例(Decoder)

package main

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

func main() {
	// 打开JSON文件
	file, err := os.Open("books.json")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	// 创建JSON解码器
	decoder := json.NewDecoder(file)

	var books []struct {
		Title  string `json:"title"`
		Author string `json:"author"`
	}

	// 解码JSON数据
	err = decoder.Decode(&books)
	if err != nil {
		panic(err)
	}

	// 打印结果
	for _, book := range books {
		fmt.Printf("%s by %s\n", book.Title, book.Author)
	}
}

6. 自定义编码/解码

可以通过实现json.Marshalerjson.Unmarshaler接口来自定义编码和解码行为。

package main

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

// 自定义时间格式
type CustomTime struct {
	time.Time
}

const layout = "2006-01-02"

// 实现Marshaler接口
func (ct CustomTime) MarshalJSON() ([]byte, error) {
	return []byte(`"` + ct.Time.Format(layout) + `"`), nil
}

// 实现Unmarshaler接口
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
	s := strings.Trim(string(data), `"`)
	t, err := time.Parse(layout, s)
	if err != nil {
		return err
	}
	ct.Time = t
	return nil
}

type Event struct {
	Name      string     `json:"name"`
	Timestamp CustomTime `json:"timestamp"`
}

func main() {
	// 编码示例
	event := Event{
		Name:      "Product Launch",
		Timestamp: CustomTime{time.Now()},
	}

	jsonData, err := json.Marshal(event)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(jsonData))
	// 输出: {"name":"Product Launch","timestamp":"2023-04-01"}

	// 解码示例
	jsonStr := `{"name":"Team Meeting","timestamp":"2023-04-15"}`
	var decodedEvent Event
	err = json.Unmarshal([]byte(jsonStr), &decodedEvent)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", decodedEvent)
	// 输出: {Name:Team Meeting Timestamp:{Time:2023-04-15 00:00:00 +0000 UTC}}
}

7. 处理JSON标签选项

JSON标签可以包含额外的选项来控制编解码行为。

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	ID        int    `json:"id"`                     // 常规字段
	Username  string `json:"username"`               // 常规字段
	Password  string `json:"-"`                      // 忽略字段
	Email     string `json:"email,omitempty"`        // 如果为空则忽略
	LastLogin int64  `json:"last_login,omitempty"`   // 如果为零值则忽略
	IsActive  bool   `json:"is_active,string"`      // 编码为字符串
}

func main() {
	user := User{
		ID:        1,
		Username:  "johndoe",
		Password:  "secret",
		LastLogin: 0,       // 零值
		IsActive:  true,
	}

	jsonData, err := json.Marshal(user)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(jsonData))
	// 输出: {"id":1,"username":"johndoe","is_active":"true"}
}

8. 处理HTML特殊字符

默认情况下,JSON编码器会转义HTML特殊字符。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	data := map[string]string{
		"message": "<script>alert('xss')</script>",
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(jsonData))
	// 输出: {"message":"\u003cscript\u003ealert('xss')\u003c/script\u003e"}
}

如果需要禁用HTML转义:

package main

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

func main() {
	data := map[string]string{
		"message": "<script>alert('xss')</script>",
	}

	buf := new(bytes.Buffer)
	encoder := json.NewEncoder(buf)
	encoder.SetEscapeHTML(false) // 禁用HTML转义
	err := encoder.Encode(data)
	if err != nil {
		panic(err)
	}

	fmt.Println(buf.String())
	// 输出: {"message":"<script>alert('xss')</script>"}
}

9. 处理原始JSON消息(RawMessage)

json.RawMessage可以用来延迟解析或传递原始JSON数据。

package main

import (
	"encoding/json"
	"fmt"
)

type Message struct {
	Type string          `json:"type"`
	Data json.RawMessage `json:"data"` // 原始JSON数据
}

type TextContent struct {
	Text string `json:"text"`
}

type ImageContent struct {
	URL  string `json:"url"`
	Alt  string `json:"alt"`
}

func main() {
	// 模拟接收到的JSON消息
	jsonStr := `{
		"type": "image",
		"data": {
			"url": "https://example.com/image.jpg",
			"alt": "Example image"
		}
	}`

	var msg Message
	err := json.Unmarshal([]byte(jsonStr), &msg)
	if err != nil {
		panic(err)
	}

	switch msg.Type {
	case "text":
		var content TextContent
		err = json.Unmarshal(msg.Data, &content)
		fmt.Println("Text:", content.Text)
	case "image":
		var content ImageContent
		err = json.Unmarshal(msg.Data, &content)
		fmt.Println("Image URL:", content.URL, "Alt:", content.Alt)
	}
	// 输出: Image URL: https://example.com/image.jpg Alt: Example image
}

10. 处理JSON数组

处理JSON数组数据。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	// JSON数组字符串
	jsonStr := `[
		{"name": "Alice", "age": 25},
		{"name": "Bob", "age": 30},
		{"name": "Charlie", "age": 35}
	]`

	// 解码到结构体切片
	var people []struct {
		Name string `json:"name"`
		Age  int    `json:"age"`
	}

	err := json.Unmarshal([]byte(jsonStr), &people)
	if err != nil {
		panic(err)
	}

	for _, p := range people {
		fmt.Printf("%s is %d years old\n", p.Name, p.Age)
	}
	/*
	输出:
	Alice is 25 years old
	Bob is 30 years old
	Charlie is 35 years old
	*/
}

总结

Go的encoding/json包提供了强大的JSON处理能力,包括:

  1. 结构体与JSON的相互转换(Marshal/Unmarshal)
  2. 流式处理(Encoder/Decoder)
  3. 自定义编解码行为
  4. 处理复杂和动态JSON结构
  5. 各种标签选项控制编解码行为