Go語言使用Json的方法實現(xiàn)
在Go語言中,處理JSON數據通常涉及編碼(將Go結構體轉換為JSON字符串)和解碼(將JSON字符串轉換為Go結構體)。Go標準庫中的encoding/json包提供了這些功能。第三方插件可以使用"github.com/goccy/go-json"也有同樣的功能
Marshal函數將會遞歸遍歷整個對象,依次按成員類型對這個對象進行編碼,類型轉換規(guī)則如下:
bool類型 轉換為JSON的Boolean整數,浮點數等數值類型 轉換為
JSON的Numberstring轉換為JSON的字符串(帶""引號)struct轉換為JSON的Object,再根據各個成員的類型遞歸打包數組或切片 轉換為
JSON的Array[]byte會先進行base64編碼然后轉換為JSON字符串map轉換為JSON的Object,key必須是stringinterface{}按照內部的實際類型進行轉換nil轉為JSON的nullchannel,func等類型 會返回UnsupportedTypeError
1、使用標準庫中的encoding/json包
字符串輸出&格式化輸出&解碼
package main
import (
"encoding/json"
"fmt"
)
type ColorGroup struct {
ID int
Name string
Colors []string
}
// 創(chuàng)建一個ColorGroup類型的變量來保存解碼后的數據
var decodedGroup ColorGroup
func main() {
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
// 將結構體編碼為JSON字符串
jsonData1, err := json.Marshal(group)
jsonData2, err := json.MarshalIndent(group, "", " ")
if err != nil {
fmt.Println("error:", err)
return
}
// 打印JSON字符串
fmt.Println(string(jsonData1))
fmt.Println(string(jsonData2))
// Output:
//{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
// {
// "ID": 1,
// "Name": "Reds",
// "Colors": [
// "Crimson",
// "Red",
// "Ruby",
// "Maroon"
// ]
// }
// 將JSON字符串解碼到ColorGroup結構體中
err = json.Unmarshal([]byte(jsonData1), &decodedGroup)
if err != nil {
fmt.Println("error:", err)
return
}
// 打印解碼后的數據
fmt.Printf("ID: %d, Name: %s, Colors: %v\n", decodedGroup.ID, decodedGroup.Name, decodedGroup.Colors)
// Output: ID: 1, Name: Reds, Colors: [Crimson Red Ruby Maroon]
fmt.Println(decodedGroup.Colors[0])
fmt.Println(decodedGroup.Colors[1])
}
2、使用第三方包
標準輸出&格式化輸出&解碼
package main
import (
"fmt"
"github.com/goccy/go-json"
"os"
)
type ColorGroup struct {
ID int
Name string
Colors []string
}
func main() {
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
b1, err := json.Marshal(group)
if err != nil {
fmt.Println("error:", err)
}
println(os.Stdout.Write(b1)) //os.Stdout.Write(b1)將字節(jié)切片b(即JSON字符串的字節(jié)表示)寫入到標準輸出
fmt.Println("---------------格式化輸出----------------")
// 使用 MarshalIndent 來格式化輸出
b2, err := json.MarshalIndent(group, "", " ") // 第二個參數是空字符串,表示不添加前綴;第三個參數是縮進字符串
if err != nil {
fmt.Println("error:", err)
return
}
// 使用 fmt.Println 來打印字符串
// MarshalIndent返回的是字節(jié)切片,我們需要使用string(b2)來將其轉換為字符串
fmt.Println(string(b2)) // 將字節(jié)切片轉換為字符串并打印
// 輸出將會是格式化后的 JSON 字符串
// 創(chuàng)建一個ColorGroup類型的變量來保存解碼后的數據
var decodedGroup ColorGroup
// 將JSON字符串解碼到ColorGroup結構體中
err = json.Unmarshal([]byte(b1), &decodedGroup)
if err != nil {
fmt.Println("error:", err)
return
}
// 打印解碼后的數據
fmt.Printf("ID: %d, Name: %s, Colors: %v\n", decodedGroup.ID, decodedGroup.Name, decodedGroup.Colors)
// Output: ID: 1, Name: Reds, Colors: [Crimson Red Ruby Maroon]
fmt.Println(decodedGroup.Colors[0])
fmt.Println(decodedGroup.Colors[1])
}
請注意,在解碼時,你需要將JSON字符串轉換為[]byte,并且傳入結構體的指針(使用&)。這樣,解碼后的數據才會被寫入到結構體中
3、decode
package main
import (
"fmt"
"github.com/goccy/go-json"
)
// Animal 定義結構體來表示單個JSON對象
type Animal struct {
Name string
Order string
}
func main() {
//創(chuàng)建一個JSON字節(jié)切片
var jsonBlob = []byte(`[
{"Name": "Platypus", "Order": "Monotremata"},
{"Name": "Quoll", "Order": "Dasyuromorphia"}
]`)
var animals []Animal
err := json.Unmarshal(jsonBlob, &animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)
fmt.Println()
// 打印解碼后的數據
for _, animal := range animals {
fmt.Printf("Name: %s, Order: %s\n", animal.Name, animal.Order)
}
}
4、注意
結構體
結構體必須是大寫字母開頭的成員才會被JSON處理到,小寫字母開頭的成員不會有影響。
Mashal時,結構體的成員變量名將會直接作為JSON Object的key打包成JSON;Unmashal時,會自動匹配對應的變量名進行賦值,大小寫不敏感。
Unmarshal時,如果JSON中有多余的字段,會被直接拋棄掉;如果JSON缺少某個字段,則直接忽略不對結構體中變量賦值,不會報錯。
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
inner string
}
func main() {
var m = Message{
Name: "Alice",
Body: "Hello",
Time: 1294706395881547000,
inner: "ok",
}
b := []byte(`{"nAmE":"Bob","Food":"Pickle", "inner":"changed"}`)
err := json.Unmarshal(b, &m)
if err != nil {
fmt.Printf(err.Error())
return
}
fmt.Printf("%v", m)
//Output: {Bob Hello 1294706395881547000 ok}
}
StructTag/結構體標簽
如果希望手動配置結構體的成員和JSON字段的對應關系,可以在定義結構體的時候給成員打標簽:
使用omitempty熟悉,如果該字段為nil或0值(數字0,字符串"",空數組[]等),則打包的JSON結果不會有這個字段。
案例一
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string `json:"msg_name"` // 對應JSON的msg_name
Body string `json:"body,omitempty"` // 如果為空置則忽略字段
Time int64 `json:"-"` // 直接忽略字段
}
func main() {
var m = Message{
Name: "Alice",
Body: "",
Time: 1294706395881547000,
}
data, err := json.Marshal(m)
if err != nil {
fmt.Printf(err.Error())
return
}
fmt.Println(string(data))
//Output:{"msg_name":"Alice"}
}
案例二
package main
import (
"encoding/json"
"fmt"
"log"
"time"
)
// 定義一個用于JSON映射的結構體
type User struct {
Name string `json:"username"` // 自定義字段名稱映射
Email string `json:"email"`
LastSeen CustomTime `json:"last_seen"` // 嵌套對象
Active bool `json:"-"` // 忽略此字段,即使JSON中存在也不解碼
}
// CustomTime 是一個用于表示時間的結構體
type CustomTime struct {
time.Time
}
// 實現(xiàn) json.Unmarshaler 接口的 UnmarshalJSON 方法
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
// 解析自定義時間格式
parsedTime, err := time.Parse(time.RFC3339, s)
if err != nil {
return err
}
ct.Time = parsedTime
return nil
}
func main() {
// 模擬從HTTP請求中獲取的JSON數據
jsonData := []byte(`{
"username": "johndoe",
"email": "john.doe@example.com",
"last_seen": "2023-04-01T12:34:56Z",
"active": true
}`)
// 創(chuàng)建一個 User 實例
var user User
// 使用 json.Unmarshal 解碼 JSON 數據
if err := json.Unmarshal(jsonData, &user); err != nil {
log.Fatal("Error unmarshaling JSON:", err)
}
// 打印解碼后的信息
fmt.Printf("Name: %s\n", user.Name)
fmt.Printf("Email: %s\n", user.Email)
fmt.Printf("Last Seen: %v\n", user.LastSeen)
// Active 字段將不會被解碼,即使JSON中存在
fmt.Printf("Active: %v\n", user.Active)
//輸出:
//Name: johndoe
//Email: john.doe@example.com
// Last Seen: 2023-04-01 12:34:56 +0000 UTC
//Active: false
}5、更靈活地使用JSON
使用json.RawMessage
json.RawMessage其實就是[]byte類型的重定義。可以進行強制類型轉換。
現(xiàn)在有這么一種場景,結構體中的其中一個字段的格式是未知的:
type Command struct {
ID int
Cmd string
Args *json.RawMessage
}使用json.RawMessage的話,Args字段在Unmarshal時不會被解析,直接將字節(jié)數據賦值給Args。我們可以能先解包第一層的JSON數據,然后根據Cmd的值,再確定Args的具體類型進行第二次Unmarshal。
這里要注意的是,一定要使用指針類型*json.RawMessage,否則在Args會被認為是[]byte類型,在打包時會被打包成base64編碼的字符串。
案例一
package main
import (
"encoding/json"
"fmt"
"log"
)
type Command struct {
ID int
Cmd string
Args *json.RawMessage // 未解析的JSON片段
}
func main() {
//json字節(jié)切片
jsonData := []byte(`{
"ID": 1,
"Cmd": "example",
"Args": ["arg1", "arg2"]
}`)
var cmd Command
//解碼/反序列化
if err := json.Unmarshal(jsonData, &cmd); err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}
fmt.Printf("Command: %+v\n", cmd)
// 如果需要,可以進一步處理cmd.Args字段
// 例如,將其解析為特定的Go類型
var args []string
if err := json.Unmarshal(*cmd.Args, &args); err != nil {
log.Printf("解析錯誤: %v", err)
} else {
fmt.Printf("Args: %v\n", args)
}
//輸出
//Command: {ID:1 Cmd:example Args:0xc0000080f0}
//Args: [arg1 arg2]
}案例二
package main
import (
"encoding/json"
"fmt"
"log"
)
type Command struct {
ID int
Cmd string
Args *json.RawMessage // 未解析的JSON片段
}
// UnmarshalJSON 自定義JSON解碼方法,Command實現(xiàn)了Unmarshaler接口
func (c *Command) UnmarshalJSON(data []byte) error {
fmt.Println("--------------使用自定義解碼--------------")
// 定義一個輔助結構體,用于解碼除Args外的其他字段
type alias Command
var aux struct {
alias // 嵌入別名類型以獲取其他字段
}
// 先解碼除Args外的所有字段
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
fmt.Printf("Command ID: %+v, Cmd: %+v\n", aux.alias.ID, aux.alias.Cmd)
// 將別名結構體中的字段復制到c中
*c = Command(aux.alias)
// 檢查JSON中是否有Args字段,并處理它
var m map[string]json.RawMessage
if err := json.Unmarshal(data, &m); err != nil {
// 如果這里出錯,可能是因為JSON格式不正確,但我們可能仍然想要保留已經解析的字段
// 因此,我們可以只記錄一個錯誤,但不返回它
log.Printf("Error parsing Args field: %v", err)
} else {
// 如果Args字段存在,將其賦值給c.Args
if rawArgs, ok := m["Args"]; ok {
c.Args = &rawArgs // 注意這里我們取了rawArgs的地址
var args []string
if err := json.Unmarshal(*c.Args, &args); err != nil {
log.Printf("Error parsing Args contents: %v", err)
} else {
fmt.Printf("Args: %v\n", args)
}
}
}
// 如果沒有錯誤,返回nil
return nil
}
func main() {
//json字節(jié)切片
jsonData := []byte(`{
"ID": 1,
"Cmd": "example",
"Args": ["arg1", "arg2"]
}`)
var cmd Command
//解碼/反序列化
if err := json.Unmarshal(jsonData, &cmd); err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}
}
案例三
package main
import (
"encoding/json"
"fmt"
"log"
)
type Command struct {
ID int
Cmd string
Args *json.RawMessage // 未解析的JSON片段
}
// UnmarshalJSON 自定義JSON解碼方法,Command實現(xiàn)了Unmarshaler接口
func (c *Command) UnmarshalJSON(data []byte) error {
fmt.Println("--------------使用自定義解碼--------------")
// 檢查JSON中是否有Args字段,并處理它
var m map[string]json.RawMessage
if err := json.Unmarshal(data, &m); err != nil {
// 如果這里出錯,可能是因為JSON格式不正確,但我們可能仍然想要保留已經解析的字段
// 因此,我們可以只記錄一個錯誤,但不返回它
log.Printf("Error parsing Args field: %v", err)
} else {
// 如果Args字段存在,將其賦值給c.Args
if rawArgs, ok := m["Args"]; ok {
c.Args = &rawArgs // 注意這里我們取了rawArgs的地址
var args []string
if err := json.Unmarshal(*c.Args, &args); err != nil {
log.Printf("Error parsing Args contents: %v", err)
} else {
fmt.Printf("Args: %v\n", args)
}
}
}
// 如果沒有錯誤,返回nil
return nil
}
func main() {
//json字節(jié)切片
jsonData := []byte(`{
"ID": 1,
"Cmd": "example",
"Args": ["arg1", "arg2"]
}`)
var cmd Command
//解碼/反序列化
if err := json.Unmarshal(jsonData, &cmd); err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}
}
調用的json.Unmarshal,并不是調用json.Unmarshaler,為什么會調用UnmarshalJSON
調用 json.Unmarshal 函數時,您并沒有直接調用 json.Unmarshaler 接口的方法。但是,json.Unmarshal 函數在內部會檢查目標類型是否實現(xiàn)了 json.Unmarshaler 接口。如果實現(xiàn)了該接口,json.Unmarshal 就會使用您為該類型定義的 UnmarshalJSON 方法來解碼 JSON 數據。
這是 json.Unmarshal 函數內部邏輯的一部分,用于確定如何解碼 JSON 數據。具體步驟如下:
json.Unmarshal接收一個字節(jié)切片(包含 JSON 數據)和一個目標值的指針。- 它首先會檢查目標值的類型是否實現(xiàn)了
json.Unmarshaler接口。 - 如果實現(xiàn)了
json.Unmarshaler接口,json.Unmarshal就會調用該類型的UnmarshalJSON方法,并將 JSON 數據的字節(jié)切片作為參數傳遞給它。 - 如果目標值沒有實現(xiàn)
json.Unmarshaler接口,json.Unmarshal就會使用默認的解碼邏輯來填充目標值的字段。
這種機制使得開發(fā)者能夠靈活地控制 JSON 數據到 Go 結構體之間的轉換過程。通過實現(xiàn) json.Unmarshaler 接口,您可以:
- 處理 JSON 數據中不存在的字段。
- 自定義字段名稱的映射規(guī)則。
- 處理 JSON 數據中的嵌套對象或數組。
- 執(zhí)行額外的驗證或數據處理邏輯。
以下是簡單的示例,展示了如何為一個類型實現(xiàn) json.Unmarshaler 接口
處理 JSON 數據中不存在的字段
假設我們有一個結構體,它能夠處理JSON中可能缺失的字段,并且為這些字段提供默認值。
在這個例子中,Age 字段在JSON中不存在,因此它將被賦予其類型的零值(對于int類型是0)。
package main
import (
"encoding/json"
"fmt"
"log"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"`
}
func main() {
var user User
// JSON 中沒有 "age" 字段,將使用 Age 的零值 0
jsonData := []byte(`{"name": "John", "email": "john@example.com"}`)
if err := json.Unmarshal(jsonData, &user); err != nil {
log.Fatal(err)
}
fmt.Printf("Name: %s, Age: %d, Email: %s\n", user.Name, user.Age, user.Email)
//Name: John, Age: 0, Email: john@example.com
}自定義字段名稱的映射規(guī)則
使用結構體標簽中的json鍵來指定JSON字段名。
在這個例子中,結構體的字段名和JSON字段名不匹配,我們通過在結構體標簽中指定json來實現(xiàn)映射。
package main
import (
"encoding/json"
"fmt"
"log"
)
type User struct {
Username string `json:"user_name"`
Password string `json:"pass"`
}
func main() {
var user User
jsonData := []byte(`{"user_name": "johndoe", "pass": "secret"}`)
if err := json.Unmarshal(jsonData, &user); err != nil {
log.Fatal(err)
}
fmt.Printf("Username: %s, Password: %s\n", user.Username, user.Password)
//Username: johndoe, Password: secret
}處理 JSON 數據中的嵌套對象或數組
解碼一個包含嵌套結構體的JSON數據。
在這個例子中,Address 是一個嵌套在 User 結構體中的對象。
package main
import (
"encoding/json"
"fmt"
"log"
)
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type User struct {
Name string `json:"name"`
Address Address `json:"address"` // 嵌套對象
}
func main() {
var user User
jsonData := []byte(`{"name": "Jane", "address": {"city": "New York", "country": "USA"}}`)
if err := json.Unmarshal(jsonData, &user); err != nil {
log.Fatal(err)
}
fmt.Printf("Name: %s, Lives in %s, %s\n", user.Name, user.Address.City, user.Address.Country)
//Name: Jane, Lives in New York, USA
}執(zhí)行額外的驗證或數據處理邏輯
在UnmarshalJSON方法中添加額外的驗證邏輯。
在這個例子中,我們?yōu)?code>User類型實現(xiàn)了自定義的UnmarshalJSON方法。在解碼過程中,如果Age字段的值是負數,將返回一個錯誤,這是一個額外的驗證邏輯。
package main
import (
"encoding/json"
"fmt"
"log"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func (u *User) UnmarshalJSON(data []byte) error {
type Alias User // 影子類型,避免遞歸調用 UnmarshalJSON
aux := &Alias{Name: u.Name, Age: u.Age} // 使用輔助結構體來解耦
if err := json.Unmarshal(data, aux); err != nil {
return err
}
*u = User(*aux) // 將解耦的結構體賦值給當前結構體
if u.Age < 0 { //年齡不能為負數
return fmt.Errorf("age cannot be negative")
}
return nil
}
func main() {
var user User
jsonData := []byte(`{"name": "Alice", "age": -5}`)
if err := json.Unmarshal(jsonData, &user); err != nil {
log.Fatal(err)
}
fmt.Printf("Name: %s, Age: %d\n", user.Name, user.Age)
} 在上面的示例中,User類型實現(xiàn)了 json.Unmarshaler 接口的 UnmarshalJSON 方法,使得 json.Unmarshal 函數在解碼 JSON 數據時會調用這個方法,而不是使用默認的解碼邏輯。這允許我們自定義解碼邏輯,例如只接受特定格式的 JSON 數據。
使用interface{}
interface{}類型在Unmarshal時,會自動將JSON轉換為對應的數據類型:
JSON的boolean 轉換為bool
JSON的數值 轉換為float64
JSON的字符串 轉換為string
JSON的Array 轉換為[]interface{}
JSON的Object 轉換為map[string]interface{}
JSON的null 轉換為nil
需要注意的有兩個。一個是所有的JSON數值自動轉換為float64類型,使用時需要再手動轉換為需要的int,int64等類型。第二個是JSON的object自動轉換為map[string]interface{}類型,訪問時直接用JSON ``Object的字段名作為key進行訪問。再不知道JSON數據的格式時,可以使用interface{}。
到此這篇關于Go語言使用Json的方法實現(xiàn)的文章就介紹到這了,更多相關Go 使用Json內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Golang使用archive/zip包實現(xiàn)ZIP壓縮與解壓
Golang?中的?archive/zip?包用于處理?ZIP?格式的壓縮文件,提供了一系列用于創(chuàng)建、讀取和解壓縮?ZIP?格式文件的函數和類型,使用起來非常方便,下面就跟隨小編一起了解一下具體使用方法吧2023-08-08
go語言實現(xiàn)sftp包上傳文件和文件夾到遠程服務器操作
這篇文章主要介紹了go語言實現(xiàn)sftp包上傳文件和文件夾到遠程服務器操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12

