Go中JSON转换与泛型

查看是否支持泛型

Go语言的泛型支持始于Go 1.18版本。

在这个版本中,Go核心团队进行了自Go语言开源以来最大的一次语法特性变更,引入了对使用参数化类型的泛型代码的支持。

查看版本

1
go version

在不支持泛型的版本里,泛型编程使用interface{}实现。

简单的JSON转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package test

import (
"encoding/json"
"fmt"
"z-wiki/utils"
)

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}

func Json2Str() {
person := Person{Name: "Alice", Age: 30}
jsonData, err := json.Marshal(person)
if err != nil {
fmt.Println("转换失败:", err)
return
}
jsonString := string(jsonData)
fmt.Println("Json2Str:", jsonString)
}

func Str2Json() {
str := `{"Name": "Alice", "Age": 30}`
var person Person
err := json.Unmarshal([]byte(str), &person)
if err != nil {
fmt.Println("转换失败:", err)
return
}
fmt.Printf("Str2Json:%+v\n", person)
}

带泛型的JSON转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package test

import (
"encoding/json"
"fmt"
"z-wiki/utils"
)

type ResultVo[T any] struct {
Code int `json:"code"`
Msg string `json:"msg"`
Obj T `json:"obj"`
}

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}

func Str2Json2() {
str := `{
"code": 0,
"msg": "success",
"obj": {
"Name": "Alice",
"Age": 30
}
}`
var result ResultVo[Person]
err := json.Unmarshal([]byte(str), &result)
if err != nil {
fmt.Println("转换失败:", err)
return
}
fmt.Printf("Str2Json2:%+v\n", result)
}

JSON转换工具类

工具类

utils/json_utils.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package utils

import (
"encoding/json"
)

// ObjToStr 对象转JSON字符串
func ObjToStr(value interface{}) (string, error) {
meta, err := json.Marshal(value)
return string(meta), err
}

// StrToObj 字符串转对象
func StrToObj(meta string, result interface{}) error {
return json.Unmarshal(StringToBytes(meta), result)
}

func StringToBytes(value string) []byte {
return []byte(value)
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package test

import (
"fmt"
"z-wiki/utils"
)

type ResultVo[T any] struct {
Code int `json:"code"`
Msg string `json:"msg"`
Obj T `json:"obj"`
}

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}

func Obj2Str() {
result := ResultVo[Person]{Code: 0, Msg: "success", Obj: Person{Name: "Alice", Age: 30}}
jsonString, err := utils.ObjToStr(result)
if err != nil {
return
}
fmt.Println("Obj2Str:", jsonString)
}

func Obj2Str2() {
result := []Person{Person{Name: "张三", Age: 18}, Person{Name: "李四", Age: 30}}
jsonString, err := utils.ObjToStr(result)
if err != nil {
return
}
fmt.Println("Obj2Str2:", jsonString)
}

func Str2Obj() {
str := `{
"code": 0,
"msg": "success",
"obj": {
"Name": "Alice",
"Age": 30
}
}`
var result ResultVo[Person]
err := utils.StrToObj(str, &result)
if err != nil {
return
}
fmt.Printf("Str2Obj:%+v\n", result)
}

func Str2Obj2() {
str := `[
{
"Name": "张三",
"Age": 30
},
{
"Name": "李四",
"Age": 18
}
]`

var result []Person
err := utils.StrToObj(str, &result)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Str2Obj2:%+v\n", result)
}

JSON转换工具类(泛型)

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package utils

import (
"encoding/json"
)

// ObjToJson 对象转JSON字符串
func ObjToJson[T any](obj T) (string, error) {
meta, err := json.Marshal(obj)
return string(meta), err
}

// JsonToObj 字符串转对象
func JsonToObj[T any](str string) (T, error) {
var result T
err := json.Unmarshal(StringToBytes(str), &result)
return result, err
}

func StringToBytes(str string) []byte {
return []byte(str)
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package test

import (
"fmt"
"z-wiki/utils"
)

type ResultVo[T any] struct {
Code int `json:"code"`
Msg string `json:"msg"`
Obj T `json:"obj"`
}

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}

func Obj2Str() {
result := ResultVo[Person]{Code: 0, Msg: "success", Obj: Person{Name: "Alice", Age: 30}}
jsonString, err := utils.ObjToJson[ResultVo[Person]](result)
if err != nil {
return
}
fmt.Println("Obj2Str:", jsonString)
}

func Obj2Str2() {
result := []Person{Person{Name: "张三", Age: 18}, Person{Name: "李四", Age: 30}}
jsonString, err := utils.ObjToJson[[]Person](result)
if err != nil {
return
}
fmt.Println("Obj2Str2:", jsonString)
}

func Str2Obj() {
str := `{
"code": 0,
"msg": "success",
"obj": {
"Name": "Alice",
"Age": 30
}
}`
result, err := utils.JsonToObj[ResultVo[Person]](str)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Str2Obj:%+v\n", result)
}

func Str2Obj2() {
str := `[
{
"Name": "张三",
"Age": 30
},
{
"Name": "李四",
"Age": 18
}
]`

result, err := utils.JsonToObj[[]Person](str)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Str2Obj2:%+v\n", result)
}

main_test.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package test

import (
"testing"
)

// 普通的测试
func Test01(t *testing.T) {
Obj2Str()
Obj2Str2()

Str2Obj()
Str2Obj2()
}

两者结合

这里对象转字符串一般不会报错,所以直接返回的字符串,不再返回错误。

对象转字符串不再传入原类型,方便调用。

字符串转对象依旧使用的泛型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package utils

import (
"encoding/json"
)

// ObjToJson 对象转JSON字符串
func ObjToJson(value interface{}) string {
meta, err := json.Marshal(value)
if err != nil {
return ""
}
return string(meta)
}

// JsonToObj 字符串转对象
func JsonToObj[T any](str string) (T, error) {
var result T
err := json.Unmarshal(StringToBytes(str), &result)
return result, err
}

func StringToBytes(str string) []byte {
return []byte(str)
}

知识点

interface{}

在 Go 语言中,interface{} 是一种空接口类型(empty interface),也被称为“空接口”。

空接口可以接受任何类型的值,因为它不限制其包含的值的类型。换句话说,空接口不包含任何方法签名,因此可以表示任何类型的值。

在 Go 中,空接口的定义如下:

1
type emptyInterface interface{}

通过空接口,可以存储任何值,因为任何值都满足空接口的要求。这使得空接口在处理未知类型、泛型编程或者需要接受任意类型参数的情况下非常有用。

下面是一个简单的示例,展示了如何使用空接口来接受任意类型的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}

func main() {
var i interface{}

i = 42
describe(i)

i = "hello"
describe(i)

i = true
describe(i)
}

在上面的示例中,describe 函数接受一个空接口参数,并打印出该参数的值和类型。

main 函数中,我们使用不同类型的值调用 describe 函数来演示空接口的灵活性。

总之,空接口 interface{} 是一种特殊的接口类型,在 Go 语言中起到了非常重要的作用,它允许我们在不知道具体类型的情况下处理各种值。

结构体

上面JSON转换的时候结构体是这样写的:

1
2
3
4
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}

在 Go 语言中,结构体(struct)中的字段类型后面的 json:"name" 是用于定义字段的 JSON 编码/解码时的特殊标签(tag)。

这种标签的作用是为结构体字段添加额外的元信息,用于在编码和解码 JSON 数据时指定字段的名称、忽略字段等属性。

在给定的示例中,json:"name"json:"age" 是用于指定字段在 JSON 编码/解码过程中对应的名称。

这意味着当使用 Go 的 encoding/json 包编码该结构体实例时,Name 字段会被编码为 JSON 对象的 name 属性,Age 字段会被编码为 JSON 对象的 age 属性。

以下是一个示例说明如何使用结构体标签进行 JSON 编码/解码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
"encoding/json"
"fmt"
)

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}

func main() {
p := Person{"Alice", 30}

// 编码结构体为 JSON
jsonData, err := json.Marshal(p)
if err != nil {
fmt.Println("JSON encode error:", err)
return
}

fmt.Println(string(jsonData)) // 输出: {"name":"Alice","age":30}

// 解码 JSON 到结构体
var p2 Person
err = json.Unmarshal(jsonData, &p2)
if err != nil {
fmt.Println("JSON decode error:", err)
return
}

fmt.Println(p2) // 输出: {Alice 30}
}

在上面的示例中,我们定义了一个Person结构体,其中每个字段都有一个类似json:"name"的标签。

使用json.Marshal函数将Person实例编码为 JSON 数据时,编码器会根据这些标签将字段名映射到 JSON 对象中的属性名。

相反,json.Unmarshal函数则依据这些标签将 JSON 数据的属性名映射回结构体的字段。

总之,类型后面的 json:"name" 是结构体字段的标签,用于定义该字段在 JSON 编码/解码过程中的属性。