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結構
- 各種標簽選項控制編解碼行為