0人参与 • 2025-02-14 • Golang
在 go 语言中,json 序列化和反序列化通常通过标准库 encoding/json 来实现。这个包提供了简单易用的接口来将 go 数据结构转换为 json 格式字符串(序列化),以及从 json 字符串解析出 go 数据结构(反序列化)。
使用 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
}
使用 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}
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.marshal() 时,go 使用反射来检查传入对象的类型。reflect.typeof() 和 reflect.valueof() 获取类型信息和实际值。json:"name"),并根据字段类型生成相应的 json 字符串。json 反序列化中的反射
json.unmarshal() 时,go 使用目标变量指针,通过反射确定需要填充的数据结构。思路:模仿 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":""}
思路
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)
}所以手动实现 json 序列化和反序列化只是帮助我们更好地理解数据格式与程序语言之间的映射关系,在实际开发中,使用标准库 encoding/json 是更高效且可靠的方法,因为它已经考虑了许多复杂情况,并进行了性能优化。
在 json简单反序列化的例子中,使用了单引号和双引号定义不同类型的字符数据。具体的区别是啥呢?
单引号 ('):
rune 是一个别名,代表 int32 类型,用于表示 unicode 码点。'a', '中', '😊'。双引号 ("):
\n, \t, \" 等。在 json简单反序列化的例子中,使用了strconv.atoi函数将字符串(string)转换为整数(int),除此之外还有strconv.parseint
strconv.atoi 函数将字符串转换为int类型。如果转换成功,它返回转换后的整数和nil错误;如果失败,它返回0和错误。strconv.parseint 函数将字符串转换为int64类型,并且允许你指定基数和位大小。如果你需要转换为int类型,你可能需要根据平台(32位或64位)来决定如何处理结果。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序列化内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论