Golang JSON 标准库用法详解
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,Go语言的标准库encoding/json
提供了强大的JSON处理能力。下面我将详细介绍各种用法并提供示例代码。
1. 基本编码(Marshal)
将Go数据结构转换为JSON字符串。
package mainimport ("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", // 这个字段不会被编码}// 将结构体编码为JSONjsonData, 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 Personerr := 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 mainimport ("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 mainimport ("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 mainimport ("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 = treturn 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 Eventerr = 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 mainimport ("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 mainimport ("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 mainimport ("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 mainimport ("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 Messageerr := json.Unmarshal([]byte(jsonStr), &msg)if err != nil {panic(err)}switch msg.Type {case "text":var content TextContenterr = json.Unmarshal(msg.Data, &content)fmt.Println("Text:", content.Text)case "image":var content ImageContenterr = 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 mainimport ("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 oldBob is 30 years oldCharlie is 35 years old*/
}
总结
Go的encoding/json
包提供了强大的JSON处理能力,包括:
- 结构体与JSON的相互转换(Marshal/Unmarshal)
- 流式处理(Encoder/Decoder)
- 自定义编解码行为
- 处理复杂和动态JSON结构
- 各种标签选项控制编解码行为