Go语言的泛型支持始于Go 1.18版本。
在这个版本中,Go核心团队进行了自Go语言开源以来最大的一次语法特性变更,引入了对使用参数化类型的泛型代码的支持。
查看版本
go version
在不支持泛型的版本里,泛型编程使用interface{}
实现。
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)
}
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)
}
utils/json_utils.go
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)
}
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)
}
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)
}
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
package test
import (
"testing"
)
// 普通的测试
func Test01(t *testing.T) {
Obj2Str()
Obj2Str2()
Str2Obj()
Str2Obj2()
}
这里对象转字符串一般不会报错,所以直接返回的字符串,不再返回错误。
对象转字符串不再传入原类型,方便调用。
字符串转对象依旧使用的泛型。
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)
}
在 Go 语言中,interface{}
是一种空接口类型(empty interface),也被称为“空接口”。
空接口可以接受任何类型的值,因为它不限制其包含的值的类型。换句话说,空接口不包含任何方法签名,因此可以表示任何类型的值。
在 Go 中,空接口的定义如下:
type emptyInterface interface{}
通过空接口,可以存储任何值,因为任何值都满足空接口的要求。这使得空接口在处理未知类型、泛型编程或者需要接受任意类型参数的情况下非常有用。
下面是一个简单的示例,展示了如何使用空接口来接受任意类型的值:
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转换的时候结构体是这样写的:
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 编码/解码:
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 编码/解码过程中的属性。