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.Marshaler
和json.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处理能力,包括:
- 结构体与JSON的相互转换(Marshal/Unmarshal)
- 流式处理(Encoder/Decoder)
- 自定义编解码行为
- 处理复杂和动态JSON结构
- 各种标签选项控制编解码行为