it编程 > 前端脚本 > Golang

golang通过反射手动实现json序列化的方法

0人参与 2025-02-14 Golang

一、json

在 go 语言中,json 序列化和反序列化通常通过标准库 encoding/json 来实现。这个包提供了简单易用的接口来将 go 数据结构转换为 json 格式字符串(序列化),以及从 json 字符串解析出 go 数据结构(反序列化)。

1.1 序列化(将 go 对象转换为 json)

使用 json.marshal() 函数可以将一个 go 对象转换为 json 字符串。

import (
	"encoding/json"
	"fmt"
	"testing"
)
type testjson struct {
	userid       int    `json:"user_id"` // 使用结构体标签可以指定字段在 json 中的键名
	usernickname string // 如果没有指定标签,则默认使用字段名。
	userage      int    `json:"age,omitempty"` // 使用omitempty可以在序列化时忽略空值
}
func test1(t *testing.t) {
	tjson1 := testjson{1, "jackson", 18}
	tjson2 := new(testjson)
	jsonstr1, _ := json.marshal(tjson1)
	jsonstr2, _ := json.marshal(tjson2)
	fmt.println(string(jsonstr1))
	fmt.println(string(jsonstr2))
}

输出

{"user_id":1,"usernickname":"jackson","age":18}
{"user_id":0,"usernickname":""}

美观打印的序列化

如果需要生成格式良好的、可读性更高的输出,可以使用 json.marshalindent()

func test2(t *testing.t) {
	tjson1 := testjson{1, "jackson", 18}
	jsonstr1, _ := json.marshalindent(tjson1, "", "\t")
	fmt.println(string(jsonstr1))
}

输出

{
    "user_id": 1,
    "usernickname": "jackson",
    "age": 18
}

1.2 反序列化(将 json 转换为 go 对象)

使用 json.unmarshal() 函数可以将一个 json 字符串解析到相应的 go 数据结构中。

func test3(t *testing.t) {
	jsonstr :=
		`{
			"user_id": 1,
			"usernickname": "jackson",
			"age": 18
		}`
	var tjson testjson
	// 将字符串转为字节数组,再传入解析后的对象指针
	// 第二个参数必须是指向目标数据类型变量的指针,以便函数能够修改该变量。
	err := json.unmarshal([]byte(jsonstr), &tjson)
	if err != nil {
		fmt.println("解析错误:", err)
	} else {
		fmt.println(tjson)
	}
}

输出

{1 jackson 18}

1.3 注意

func test5(t *testing.t) {
	jsonstr :=
		`{
			"user_id": 1,
			"usernickname": "jackson",
			"age": 18,
			"addr": ["地址1","地址2"],
			"info":{
				"id":"2",
				"name":"a"
			}
		}`
	var tjson testjson
	err := json.unmarshal([]byte(jsonstr), &tjson)
	if err != nil {
		fmt.println("解析错误:", err)
	} else {
		fmt.println(tjson)
	}
}

输出

{1 jackson 18}

灵活性:对于未知或动态数据,可以考虑使用 map 或 interface{} 来接收解码结果,但这会丧失一些类型安全特性。

func test4(t *testing.t) {
	jsonstr :=
		`{
			"user_id": 1,
			"usernickname": "jackson",
			"age": 18,
			"addr": ["地址1","地址2"],
			"info":{
				"id":"2",
				"name":"a"
			}
		}`
	var tjson map[string]any
	err := json.unmarshal([]byte(jsonstr), &tjson)
	if err != nil {
		fmt.println("解析错误:", err)
	} else {
		fmt.println(tjson)
	}
}

输出

map[usernickname:jackson addr:[地址1 地址2] age:18 info:map[id:2 name:a] user_id:1]

二、反射手动实现

encoding/json 包在内部大量使用了反射来实现其功能。

json 序列化中的反射

json 反序列化中的反射

2.1 json简单序列化

思路:模仿 encoding/json 库,读取结构体标签指定序列化的字段名

func serializesimple(data interface{}) string {
	var resultstr string = "{"
	//1、反射传入的结构体
	reflectdatavalue := reflect.valueof(data)
	reflectdatatype := reflectdatavalue.type()
	if reflectdatavalue.kind() == reflect.ptr {
		reflectdatavalue = reflectdatavalue.elem()
	}
	if reflectdatatype.kind() == reflect.ptr {
		reflectdatatype = reflectdatatype.elem()
	}
	//2、获取字段
	for i := 0; i < reflectdatatype.numfield(); i++ {
		field := reflectdatatype.field(i)
		// 字段名
		var filedname = field.name
		// 字段的值
		filedvalue := reflectdatavalue.field(i).interface()
		//3、判断字段的标签
		if value, ok := field.tag.lookup("json"); ok {
			// 如果有json标签,则使用其定义的命名
			filedname = strings.replaceall(value, ",omitempty", "")
			// 是否忽略空值
			if strings.contains(value, "omitempty") {
				if filedvalue == "" || filedvalue == 0 {
					continue
				}
			}
		}
		// 拼接json
		resultstr += fmt.sprintf("\"%s\":\"%v\"", filedname, filedvalue)
		resultstr += ","
	}
	//4、拼接字符串
	resultstr += "}"
	// 去掉结尾的,号
	return strings.replaceall(resultstr, ",}", "}")
}
func test6(t *testing.t) {
	tjson1 := testjson{1, "jackson", 18}
	tjson2 := new(testjson)
	jsonstr1, _ := json.marshal(tjson1)
	jsonstr2, _ := json.marshal(tjson2)
	fmt.println(string(jsonstr1))
	fmt.println(string(jsonstr2))
	fmt.println("=========自定义实现=========")
	fmt.println(serializesimple(tjson1))
	fmt.println(serializesimple(tjson2))
}

输出

{"user_id":1,"usernickname":"jackson","age":18}
{"user_id":0,"usernickname":""}
=========自定义实现=========
{"user_id":"1","usernickname":"jackson","age":"18"}
{"user_id":"0","usernickname":""}

2.2 json简单反序列化

思路

func parsesimple(str string, datatemp interface{}) {
	// 判断是否标准的json格式数据
	if str != "" && str[0] == '{' && str[len(str)-1] == '}' {
		// 替换掉前后的{}
		str = strings.replaceall(strings.replaceall(str, "{", ""), "}", "")
		// 将结构体的标签解析后,塞入map中备用 :map [字段名] 字段地址
		structmap := map[string]reflect.value{}
		// 通过反射获取字段名
		rvalue := reflect.valueof(datatemp)
		if rvalue.kind() == reflect.ptr {
			rvalue = rvalue.elem()
		}
		rtype := rvalue.type()
		for i := 0; i < rtype.numfield(); i++ {
			name := rtype.field(i).name
			// 如果有定义标签,则使用标签的字段名
			if lookup, ok := rtype.field(i).tag.lookup("json"); ok {
				name = strings.replaceall(lookup, ",omitempty", "")
			}
			// 将字段名和值映射起来
			structmap[name] = rvalue.field(i)
		}
		// 按照,切割每个键值对
		splitlist := strings.split(str, ",")
		for i := range splitlist {
			s := splitlist[i]
			// 按照:切割出key 和 value
			keyvalue := strings.split(s, ":")
			key := keyvalue[0]
			key = strings.replaceall(strings.trimspace(key), "\"", "") // 去除前后的空格
			value := keyvalue[1]
			value = strings.replaceall(strings.trimspace(value), "\"", "")
			// 按照key将value塞回结构体
			switch structmap[key].type().kind() {
			// 注意判断类型,目前只写int和string,其他的类似
			case reflect.int:
				intvalue, _ := strconv.atoi(value)
				structmap[key].setint(int64(intvalue))
			case reflect.string:
				structmap[key].setstring(value)
			default:
				panic("暂不支持的数据类型")
			}
		}
	}
}
func test7(t *testing.t) {
	jsonstr :=
		`{
			"user_id": 1,
			"usernickname": "jackson",
			"age": 18
		}`
	var tjson1 testjson
	var tjson2 testjson
	err := json.unmarshal([]byte(jsonstr), &tjson1)
	if err != nil {
		fmt.println("解析错误:", err)
	} else {
		fmt.println(tjson1)
	}
	fmt.println("=========自定义实现=========")
	parsesimple(jsonstr, &tjson2)
	fmt.println(tjson2)
}

2.3 问题

所以手动实现 json 序列化和反序列化只是帮助我们更好地理解数据格式与程序语言之间的映射关系,在实际开发中,使用标准库 encoding/json 是更高效且可靠的方法,因为它已经考虑了许多复杂情况,并进行了性能优化。

2.4 总结

2.4.1 单引号和双引号定义的字符串有什么区别

在 json简单反序列化的例子中,使用了单引号和双引号定义不同类型的字符数据。具体的区别是啥呢?

单引号 (')

双引号 (")

2.4.2 string如何转为int

在 json简单反序列化的例子中,使用了strconv.atoi函数将字符串(string)转换为整数(int),除此之外还有strconv.parseint

func test8(t *testing.t) {
	str := "123"
	// 第二个参数是基数,10表示十进制
	// 第三个参数是位大小,0表示int64,32表示int32,使用32或64取决于你的系统架构
	num, err := strconv.parseint(str, 10, 0)
	if err != nil {
		fmt.println("转换错误:", err)
	} else {
		fmt.println("转换结果:", num)
		// 如果你需要int32,可以这样转换
		num32 := int32(num)
		fmt.println("转换为int32结果:", num32)
		// 如果你需要int,可以这样转换(注意:在32位系统上这将是int32,在64位系统上这将是int64)
		numint := int(num)
		fmt.println("转换为int结果:", numint)
	}
}

输出:

转换结果: 123
转换为int32结果: 123
转换为int结果: 123

到此这篇关于golang 通过反射手动实现json序列化的文章就介绍到这了,更多相关golang json序列化内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

您想发表意见!!点此发布评论

推荐阅读

go 集成nacos注册中心、配置中心的过程详解

02-14

vscode 通过Go:Install/Update Tools命令安装失败的问题解决

02-14

基于go中fyne gui的通达信数据导出工具详解

02-14

Go语言如何获取goroutine的id

02-14

Go语言中字符串赋值中的问题与解决方法

02-14

go中的参数传递是值传递还是引用传递的实现

02-14

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论