In Go, processing JSON data usually involves encoding (converting Go structs to JSON strings) and decoding (converting JSON strings to Go structs). Go standard libraryencoding/json
The package provides these features. Third-party plug-ins can use "/goccy/go-json" and have the same function
Marshal
The function will recursively traverse the entire object and encode the object according to its member type in turn. The type conversion rules are as follows:
bool
Type Convert toJSON
ofBoolean
Integer, floating point number and other numerical types Convert to
JSON
ofNumber
string
Convert toJSON
string (with ""quotes)struct
Convert toJSON
ofObject
, and then recursively package according to the type of each memberArray or slice Convert to
JSON
ofArray
[]byte
Will do it firstbase64
Encoding and converting toJSON
Stringmap
Convert toJSON
ofObject
,key
Must bestring
interface{}
Convert according to the internal actual typenil
Convert toJSON
ofnull
channel
,func
Types such as returnUnsupportedTypeError
1. Use the encoding/json package in the standard library
String output & format output & decoding
package main import ( "encoding/json" "fmt" ) type ColorGroup struct { ID int Name string Colors []string } // Create a ColorGroup type variable to save the decoded datavar decodedGroup ColorGroup func main() { group := ColorGroup{ ID: 1, Name: "Reds", Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, } // Encode the structure into a JSON string jsonData1, err := (group) jsonData2, err := (group, "", " ") if err != nil { ("error:", err) return } // Print JSON string (string(jsonData1)) (string(jsonData2)) // Output: //{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} // { // "ID": 1, // "Name": "Reds", // "Colors": [ // "Crimson", // "Red", // "Ruby", // "Maroon" // ] // } // Decode the JSON string into the ColorGroup structure err = ([]byte(jsonData1), &decodedGroup) if err != nil { ("error:", err) return } // Print the decoded data ("ID: %d, Name: %s, Colors: %v\n", , , ) // Output: ID: 1, Name: Reds, Colors: [Crimson Red Ruby Maroon] ([0]) ([1]) }
2. Use third-party packages
Standard Output & Format Output & Decoding
package main import ( "fmt" "/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 := (group) if err != nil { ("error:", err) } println((b1)) //(b1) Write byte slice b (i.e. byte representation of JSON string) to standard output ("------------------------------------------------------------------------------------------------------------------------------) // Use MarshalIndent to format the output b2, err := (group, "", " ") // The second parameter is an empty string, indicating that no prefix is added; the third parameter is an indented string if err != nil { ("error:", err) return } // Use to print strings // MarshalIndent returns a byte slice, we need to use string(b2) to convert it to a string (string(b2)) // Convert byte slices to strings and print // The output will be a formatted JSON string // Create a ColorGroup type variable to save the decoded data var decodedGroup ColorGroup // Decode the JSON string into the ColorGroup structure err = ([]byte(b1), &decodedGroup) if err != nil { ("error:", err) return } // Print the decoded data ("ID: %d, Name: %s, Colors: %v\n", , , ) // Output: ID: 1, Name: Reds, Colors: [Crimson Red Ruby Maroon] ([0]) ([1]) }
Note that when decoding, you need to convert the JSON string to[]byte
, and pass the pointer to the structure (using&
). In this way, the decoded data will be written into the structure.
3、decode
package main import ( "fmt" "/goccy/go-json" ) // Animal defines a structure to represent a single JSON objecttype Animal struct { Name string Order string } func main() { //Create a JSON byte slice var jsonBlob = []byte(`[ {"Name": "Platypus", "Order": "Monotremata"}, {"Name": "Quoll", "Order": "Dasyuromorphia"} ]`) var animals []Animal err := (jsonBlob, &animals) if err != nil { ("error:", err) } ("%+v", animals) () // Print the decoded data for _, animal := range animals { ("Name: %s, Order: %s\n", , ) } }
4. Pay attention
Structure
The structure must be a member starting with a capital letter to beJSON
Processing, members starting with lowercase letters will not have any effect.
Mashal
When the structure's member variable name will be directly used asJSON
Object
ofkey
Packed intoJSON
;Unmashal
When , the corresponding variable name will be automatically matched for assignment, and the upper and lower case is insensitive.
Unmarshal
When, ifJSON
If there are extra fields in theJSON
If a field is missing, the variables in the structure are not assigned to the structure and no error will be reported.
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 := (b, &m) if err != nil { (()) return } ("%v", m) //Output: {Bob Hello 1294706395881547000 ok} }
StructTag/Struct tag
If you want to manually configure the members of the structure andJSON
For fields correspondence, you can tag members when defining structures:
useomitempty
Familiar, if the field isnil
or 0 value (number 0, string "", empty array[], etc.), then packagedJSON
This field will not be present as a result.
Case 1
package main import ( "encoding/json" "fmt" ) type Message struct { Name string `json:"msg_name"` // msg_name corresponding to JSON Body string `json:"body,omitempty"` // If empty, ignore the field Time int64 `json:"-"` // Ignore fields directly} func main() { var m = Message{ Name: "Alice", Body: "", Time: 1294706395881547000, } data, err := (m) if err != nil { (()) return } (string(data)) //Output:{"msg_name":"Alice"} }
Case 2
package main import ( "encoding/json" "fmt" "log" "time" ) // Define a structure for JSON mappingtype User struct { Name string `json:"username"` // Custom field name mapping Email string `json:"email"` LastSeen CustomTime `json:"last_seen"` // Nested objects Active bool `json:"-"` // Ignore this field and will not decode even if it exists in JSON} // CustomTime is a structure used to represent timetype CustomTime struct { } // UnmarshalJSON method to implement interfacefunc (ct *CustomTime) UnmarshalJSON(data []byte) error { var s string if err := (data, &s); err != nil { return err } // parse custom time format parsedTime, err := (time.RFC3339, s) if err != nil { return err } = parsedTime return nil } func main() { // Simulate JSON data obtained from HTTP requests jsonData := []byte(`{ "username": "johndoe", "email": "@", "last_seen": "2023-04-01T12:34:56Z", "active": true }`) // Create a User instance var user User // Use Decode JSON Data if err := (jsonData, &user); err != nil { ("Error unmarshaling JSON:", err) } // Print the decoded information ("Name: %s\n", ) ("Email: %s\n", ) ("Last Seen: %v\n", ) // Active fields will not be decoded even if they exist in JSON ("Active: %v\n", ) //Output: //Name: johndoe //Email: @ // Last Seen: 2023-04-01 12:34:56 +0000 UTC //Active: false }
5. Use JSON more flexibly
use
Actually it's
[]byte
Redefinition of type. Cases can be performed.
There is now a scenario where the format of one of the fields in the structure is unknown:
type Command struct { ID int Cmd string Args * }
useIf so,
Args
The fields are inUnmarshal
It will not be parsed, and the byte data will be assigned directly to theArgs
. We may unpack the first layer firstJSON
Data, then according toCmd
The value ofArgs
The specific type ofUnmarshal
。
It should be noted here that you must use pointer type*
, otherwiseArgs
Will be considered[]byte
Type, when packaged, it will be packaged intobase64
Encoded string.
Case 1
package main import ( "encoding/json" "fmt" "log" ) type Command struct { ID int Cmd string Args * // Unparsed JSON fragment} func main() { //json byte slice jsonData := []byte(`{ "ID": 1, "Cmd": "example", "Args": ["arg1", "arg2"] }`) var cmd Command //Decode/deserialize if err := (jsonData, &cmd); err != nil { ("Error unmarshaling JSON: %v", err) } ("Command: %+v\n", cmd) // If necessary, the fields can be processed further // For example, parse it into a specific Go type var args []string if err := (*, &args); err != nil { ("Parse error: %v", err) } else { ("Args: %v\n", args) } //Output //Command: {ID:1 Cmd:example Args:0xc0000080f0} //Args: [arg1 arg2] }
Case 2
package main import ( "encoding/json" "fmt" "log" ) type Command struct { ID int Cmd string Args * // Unparsed JSON fragment} // UnmarshalJSON Custom JSON decoding method, Command implements the Unmarshaler interfacefunc (c *Command) UnmarshalJSON(data []byte) error { ("--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------) // Define an auxiliary structure to decode other fields except Args type alias Command var aux struct { alias // Embed alias type to get other fields } // Decode all fields except Args first if err := (data, &aux); err != nil { return err } ("Command ID: %+v, Cmd: %+v\n", , ) // Copy fields in the alias structure into c *c = Command() // Check if there is an Args field in JSON and process it var m map[string] if err := (data, &m); err != nil { // If something goes wrong here, it may be because the JSON format is incorrect, but we may still want to keep the already parsed fields // So we can just log an error but not return it ("Error parsing Args field: %v", err) } else { // If the Args field exists, assign it to if rawArgs, ok := m["Args"]; ok { = &rawArgs // Note that we took the address of rawArgs here var args []string if err := (*, &args); err != nil { ("Error parsing Args contents: %v", err) } else { ("Args: %v\n", args) } } } // If there is no error, return nil return nil } func main() { //json byte slice jsonData := []byte(`{ "ID": 1, "Cmd": "example", "Args": ["arg1", "arg2"] }`) var cmd Command //Decode/deserialize if err := (jsonData, &cmd); err != nil { ("Error unmarshaling JSON: %v", err) } }
Case Three
package main import ( "encoding/json" "fmt" "log" ) type Command struct { ID int Cmd string Args * // Unparsed JSON fragment} // UnmarshalJSON Custom JSON decoding method, Command implements the Unmarshaler interfacefunc (c *Command) UnmarshalJSON(data []byte) error { ("--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------) // Check if there is an Args field in JSON and process it var m map[string] if err := (data, &m); err != nil { // If something goes wrong here, it may be because the JSON format is incorrect, but we may still want to keep the already parsed fields // So we can just log an error but not return it ("Error parsing Args field: %v", err) } else { // If the Args field exists, assign it to if rawArgs, ok := m["Args"]; ok { = &rawArgs // Note that we took the address of rawArgs here var args []string if err := (*, &args); err != nil { ("Error parsing Args contents: %v", err) } else { ("Args: %v\n", args) } } } // If there is no error, return nil return nil } func main() { //json byte slice jsonData := []byte(`{ "ID": 1, "Cmd": "example", "Args": ["arg1", "arg2"] }`) var cmd Command //Decode/deserialize if err := (jsonData, &cmd); err != nil { ("Error unmarshaling JSON: %v", err) } }
The call is not a call, why is UnmarshalJSON called
CallWhen you are functioning, you do not call it directly
Interface method. but,
The function internally checks whether the target type is implemented.
Interface. If the interface is implemented,
The one you defined for this type will be used
UnmarshalJSON
Method to decode JSON data.
This isPart of the internal logic of the function, used to determine how to decode JSON data. The specific steps are as follows:
-
Receives a byte slice (including JSON data) and a pointer to the target value.
- It will first check whether the target value type is implemented.
Interface.
- If it is implemented
Interface,
This type of call will be called
UnmarshalJSON
method and pass the byte slice of JSON data as a parameter to it. - If the target value is not implemented
Interface,
The default decoding logic will be used to fill the field of the target value.
This mechanism allows developers to flexibly control the conversion process between JSON data and Go structures. By implementingInterface, you can:
- Process fields that do not exist in JSON data.
- Customize the mapping rules for field names.
- Processes nested objects or arrays in JSON data.
- Perform additional verification or data processing logic.
The following is a simple example showing how to implement it for a typeInterface
Handle fields that do not exist in JSON data
Suppose we have a struct that can handle fields that may be missing in JSON and provide default values for those fields.
In this example,Age
The field does not exist in JSON, so it will be given a zero value of its type (forint
Type is0
)。
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 // There is no "age" field in JSON, and the zero value of Age will be used jsonData := []byte(`{"name": "John", "email": "john@"}`) if err := (jsonData, &user); err != nil { (err) } ("Name: %s, Age: %d, Email: %s\n", , , ) //Name: John, Age: 0, Email: john@ }
Mapping rules for custom field names
Use the structure tagjson
Key to specify the JSON field name.
In this example, the field name of the structure does not match the JSON field name, we specify it in the structure labeljson
to implement mapping.
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 := (jsonData, &user); err != nil { (err) } ("Username: %s, Password: %s\n", , ) //Username: johndoe, Password: secret }
Handle nested objects or arrays in JSON data
Decodes a JSON data containing a nested structure.
In this example,Address
is a nested inUser
Objects in the structure.
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"` // Nested objects} func main() { var user User jsonData := []byte(`{"name": "Jane", "address": {"city": "New York", "country": "USA"}}`) if err := (jsonData, &user); err != nil { (err) } ("Name: %s, Lives in %s, %s\n", , , ) //Name: Jane, Lives in New York, USA }
Perform additional verification or data processing logic
existUnmarshalJSON
Add additional verification logic to the method.
In this example, weUser
Types are customizedUnmarshalJSON
method. During the decoding process, ifAge
The value of the field is negative and an error will be returned, which is an additional verification logic.
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 // Shadow type, avoid recursive calls to UnmarshalJSON aux := &Alias{Name: , Age: } // Use auxiliary structure to decouple if err := (data, aux); err != nil { return err } *u = User(*aux) // Assign the decoupled structure to the current structure if < 0 { //Age cannot be negative return ("age cannot be negative") } return nil } func main() { var user User jsonData := []byte(`{"name": "Alice", "age": -5}`) if err := (jsonData, &user); err != nil { (err) } ("Name: %s, Age: %d\n", , ) }
In the above example,User
Type is implementedInterface
UnmarshalJSON
Method, so thatThe function calls this method when decoding JSON data instead of using the default decoding logic. This allows us to customize the decoding logic, such as accepting only JSON data in a specific format.
Using interface{}
interface{}
Type inUnmarshal
When theJSON
Convert to the corresponding data type:
JSON's boolean to bool
Convert JSON's value to float64
Convert JSON string to string
Convert JSON's Array to []interface{}
JSON's Object Convert to map[string]interface{}
JSON's null conversion to nil
There are two things to note. One is allJSON
Automatically convert the value tofloat64
Type, it needs to be manually converted to requiredint
,int64
etc. The second one isJSON
ofobject
Automatically convert tomap[string]interface{}
Type, use directly when accessingJSON ``Object
The field name askey
Make an access. Don't know anymoreJSON
When the data format is used, you can use itinterface{}
。
This is the end of this article about the implementation of Go using Json. For more related content related to Go using Json, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!