SoFunction
Updated on 2025-03-05

Go language uses Json method to implement it

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/jsonThe package provides these features. Third-party plug-ins can use "/goccy/go-json" and have the same function

 MarshalThe 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:

  • boolType Convert toJSONofBoolean

  • Integer, floating point number and other numerical types Convert toJSONofNumber

  • stringConvert toJSONstring (with ""quotes)

  • structConvert toJSONofObject, and then recursively package according to the type of each member

  • Array or slice Convert toJSONofArray

  • []byteWill do it firstbase64Encoding and converting toJSONString

  • mapConvert toJSONofObjectkeyMust bestring

  • interface{}Convert according to the internal actual type

  • nilConvert toJSONofnull

  • channel,funcTypes 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 beJSONProcessing, members starting with lowercase letters will not have any effect.

MashalWhen the structure's member variable name will be directly used asJSON ObjectofkeyPacked intoJSONUnmashalWhen , the corresponding variable name will be automatically matched for assignment, and the upper and lower case is insensitive.

UnmarshalWhen, ifJSONIf there are extra fields in theJSONIf 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 andJSONFor fields correspondence, you can tag members when defining structures:

useomitemptyFamiliar, if the field isnilor 0 value (number 0, string "", empty array[], etc.), then packagedJSONThis 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[]byteRedefinition 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,ArgsThe fields are inUnmarshalIt will not be parsed, and the byte data will be assigned directly to theArgs. We may unpack the first layer firstJSONData, then according toCmdThe value ofArgsThe specific type ofUnmarshal

It should be noted here that you must use pointer type*, otherwiseArgsWill be considered[]byteType, when packaged, it will be packaged intobase64Encoded 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 directlyInterface 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 usedUnmarshalJSONMethod 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 implementedInterface,This type of call will be calledUnmarshalJSONmethod and pass the byte slice of JSON data as a parameter to it.
  • If the target value is not implementedInterface,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,AgeThe field does not exist in JSON, so it will be given a zero value of its type (forintType 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 tagjsonKey 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 labeljsonto 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,Addressis a nested inUserObjects 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

existUnmarshalJSONAdd additional verification logic to the method.

In this example, weUserTypes are customizedUnmarshalJSONmethod. During the decoding process, ifAgeThe 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,UserType is implementedInterfaceUnmarshalJSONMethod, 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 inUnmarshalWhen theJSONConvert 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 allJSONAutomatically convert the value tofloat64Type, it needs to be manually converted to requiredintint64etc. The second one isJSONofobjectAutomatically convert tomap[string]interface{}Type, use directly when accessingJSON ``ObjectThe field name askeyMake an access. Don't know anymoreJSONWhen 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!