SoFunction
Updated on 2025-03-04

Golang manually implements json serialization through reflection

1. json

In Go, JSON serialization and deserialization are usually passed through the standard libraryencoding/jsonTo achieve it. This package provides an easy-to-use interface to convert Go data structures into JSON format strings (serialized) and parse Go data structures from JSON strings (deserialized).

1.1 Serialization (convert Go objects to JSON)

use()Functions can convert a Go object to a JSON string.

import (
	"encoding/json"
	"fmt"
	"testing"
)
type TestJson struct {
	UserId       int    `json:"user_id"` // Use the structure tag to specify the key name of the field in JSON	UserNickname string // If no tag is specified, the field name is used by default.	UserAge      int    `json:"age,omitempty"` // Use omitempty to ignore null values ​​during serialization}
func Test1(t *) {
	tJson1 := TestJson{1, "Jackson", 18}
	tJson2 := new(TestJson)
	jsonStr1, _ := (tJson1)
	jsonStr2, _ := (tJson2)
	(string(jsonStr1))
	(string(jsonStr2))
}

Output

{"user_id":1,"UserNickname":"Jackson","age":18}
{"user_id":0,"UserNickname":""}

Serialization of beautiful printing

If you need to generate well-formatted and more readable output, you can use()

func Test2(t *) {
	tJson1 := TestJson{1, "Jackson", 18}
	jsonStr1, _ := (tJson1, "", "\t")
	(string(jsonStr1))
}

Output

{
    "user_id": 1,
    "UserNickname": "Jackson",
    "age": 18
}

1.2 Deserialization (convert JSON to Go object)

use()Functions can parse a JSON string into the corresponding Go data structure.

func Test3(t *) {
	jsonStr :=
		`{
			"user_id": 1,
			"UserNickname": "Jackson",
			"age": 18
		}`
	var tJson TestJson
	// Convert the string to a byte array and pass it into the parsed object pointer	// The second parameter must be a pointer to the target data type variable so that the function can modify the variable.	err := ([]byte(jsonStr), &tJson)
	if err != nil {
		("Parse error:", err)
	} else {
		(tJson)
	}
}

Output

{1 Jackson 18}

1.3 Note

  • Field Export: Only the exported fields (i.e., the capitalization of the initial letter) can be encoded/decoded.
  • Error handling: Always check for return errors to ensure the data is processed correctly.
  • Extra information: Fields outside the parsed structure will be discarded
func Test5(t *) {
	jsonStr :=
		`{
			"user_id": 1,
			"UserNickname": "Jackson",
			"age": 18,
			"addr": ["Address 1","Address 2"],
			"info":{
				"id":"2",
				"name":"a"
			}
		}`
	var tJson TestJson
	err := ([]byte(jsonStr), &tJson)
	if err != nil {
		("Parse error:", err)
	} else {
		(tJson)
	}
}

Output

{1 Jackson 18}

flexibility: For unknown or dynamic data, you can consider using map or interface{} to receive decoded results, but this will lose some type-safety features.

func Test4(t *) {
	jsonStr :=
		`{
			"user_id": 1,
			"UserNickname": "Jackson",
			"age": 18,
			"addr": ["Address 1","Address 2"],
			"info":{
				"id":"2",
				"name":"a"
			}
		}`
	var tJson map[string]any
	err := ([]byte(jsonStr), &tJson)
	if err != nil {
		("Parse error:", err)
	} else {
		(tJson)
	}
}

Output

map[UserNickname:Jackson addr:[Address 1 Address 2] age:18 info:map[id:2 name:a] user_id:1]

2. Manual reflection implementation

encoding/jsonThe package uses reflection heavily internally to achieve its functionality.

Reflection in JSON serialization

  • Calling()When Go uses reflection to check the type of incoming object.
  • pass()and()Get type information and actual values.
  • Iterate through the structure field and read the label (such asjson:"name") and generate the corresponding JSON string according to the field type.

Reflection in JSON deserialization

  • Calling()When Go uses the target variable pointer to determine the data structure to be filled through reflection.
  • According to the key names in the JSON data, find the corresponding structure field through the label map and set its value.

2.1 json simple serialization

Idea: Imitationencoding/jsonLibrary, read the field name specified by the structure tag

  • Reflected incoming structure
  • Get fields
  • Tags for judged fields
  • Stitching strings
func serializeSimple(data interface{}) string {
	var resultStr string = "{"
	//1. Reflected incoming structure	reflectDataValue := (data)
	reflectDataType := ()
	if () ==  {
		reflectDataValue = ()
	}
	if () ==  {
		reflectDataType = ()
	}
	//2. Get the field	for i := 0; i < (); i++ {
		field := (i)
		// Field name		var filedName = 
		// field value		filedValue := (i).Interface()
		//3. Determine the label of the field		if value, ok := ("json"); ok {
			// If there is a json tag, use the name of its definition			filedName = (value, ",omitempty", "")
			// Is it ignoring the empty value			if (value, "omitempty") {
				if filedValue == "" || filedValue == 0 {
					continue
				}
			}
		}
		// Stitching json		resultStr += ("\"%s\":\"%v\"", filedName, filedValue)
		resultStr += ","
	}
	//4. Splice string	resultStr += "}"
	// Remove the ending number	return (resultStr, ",}", "}")
}
func Test6(t *) {
	tJson1 := TestJson{1, "Jackson", 18}
	tJson2 := new(TestJson)
	jsonStr1, _ := (tJson1)
	jsonStr2, _ := (tJson2)
	(string(jsonStr1))
	(string(jsonStr2))
	("=========Custom implementation=========")
	(serializeSimple(tJson1))
	(serializeSimple(tJson2))
}

Output

{"user_id":1,"UserNickname":"Jackson","age":18}
{"user_id":0,"UserNickname":""}
=========== Custom implementation=============
{"user_id":"1","UserNickname":"Jackson","age":"18"}
{"user_id":"0","UserNickname":""}

2.2 json simple deserialization

Ideas

  • Cut incoming strings and split them into key value pairs
  • Reflect structure, determine the structure label and generate a map of the field and value
  • Determine the field type and set the value of the corresponding type
func parseSimple(str string, dataTemp interface{}) {
	// JSON format data to determine whether it is standard	if str != "" && str[0] == '{' && str[len(str)-1] == '}' {
		// Replace the {} before and after		str = ((str, "{", ""), "}", "")
		// After parsing the label of the structure, stuff it into the map alternate: map [field name] field address		structMap := map[string]{}
		// Get field name through reflection		rValue := (dataTemp)
		if () ==  {
			rValue = ()
		}
		rType := ()
		for i := 0; i < (); i++ {
			name := (i).Name
			// If there is a defined tag, use the field name of the tag			if lookup, ok := (i).("json"); ok {
				name = (lookup, ",omitempty", "")
			}
			// Map field names and values			structMap[name] = (i)
		}
		// According to, cut each key value pair		splitList := (str, ",")
		for i := range splitList {
			s := splitList[i]
			// According to: Cut out key and value			keyValue := (s, ":")
			key := keyValue[0]
			key = ((key), "\"", "") // Remove the spaces before and after			value := keyValue[1]
			value = ((value), "\"", "")
			// Push value back into the structure according to the key			switch structMap[key].Type().Kind() {
			// Pay attention to the type of judgment. Currently, only int and string are written, and other similar			case :
				intValue, _ := (value)
				structMap[key].SetInt(int64(intValue))
			case :
				structMap[key].SetString(value)
			default:
				panic("Data types not supported yet")
			}
		}
	}
}
func Test7(t *) {
	jsonStr :=
		`{
			"user_id": 1,
			"UserNickname": "Jackson",
			"age": 18
		}`
	var tJson1 TestJson
	var tJson2 TestJson
	err := ([]byte(jsonStr), &tJson1)
	if err != nil {
		("Parse error:", err)
	} else {
		(tJson1)
	}
	("=========Custom implementation=========")
	parseSimple(jsonStr, &tJson2)
	(tJson2)
}

2.3 Problem

  • Only process structure-type data and does not support nested complex data structures (such as slices or mappings)
  • In the serialized implementation, integers or booleans are converted to quoted strings.
  • Complex structures do not support them, problems may occur

Therefore, manually implementing JSON serialization and deserialization only helps us better understand the mapping relationship between data format and programming language. In actual development, use standard librariesencoding/jsonis a more efficient and reliable approach as it has taken into account many complex situations and has performed performance optimization.

2.4 Summary

2.4.1 What is the difference between single and double quotes defined strings

In the example of json simple deserialization, single and double quotes are used to define different types of character data. What are the specific differences?

Single quotes (')

  • Single quotes are used to represent a character (rune). In Go,runeis an alias, representingint32Type, used to represent Unicode code points.
  • A character constant enclosed in single quotes can only contain one character. For example:'a', 'middle', '😊'

Double quotes (")

  • Double quotes are used to define strings. A string is a data type composed of a series of bytes and can contain zero or more characters.
  • A string may include escape sequences, for example:\n, \t, \"wait.
  • For example: "hello", "world", "Go is fun\n".

2.4.2 How to convert string to int

In the example of json simple deserialization,The function will string (string) converts to integer (int), in addition to this

  • Function converts string tointtype. If the conversion is successful, it returns the converted integer andnilError; if it fails, it returns0and error.
  • Function converts string toint64type and allows you to specify cardinality and bit size. If you need to convert tointType, you may need to decide how to handle the results based on the platform (32-bit or 64-bit).
func Test8(t *) {
	str := "123"
	// The second parameter is the cardinality, 10 means decimal	// The third parameter is the bit size, 0 means int64, 32 means int32, using 32 or 64 depends on your system architecture	num, err := (str, 10, 0)
	if err != nil {
		("Conversion error:", err)
	} else {
		("Conversion result:", num)
		// If you need int32, you can convert it like this		num32 := int32(num)
		("Convert to int32 result:", num32)
		// If you need int, you can convert it like this (note: on 32-bit systems this will be int32, on 64-bit systems this will be int64)		numInt := int(num)
		("Convert to int result:", numInt)
	}
}

Output:

Conversion result: 123
Convert to int32 result: 123
Convert to int result: 123

This is the article about golang manually implementing json serialization through reflection. For more related golang json serialization content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!