Go语言结构转换

这里我们讲一下Go语言中,结构体对象、map[string]interface{}JSON字符串这三者的相互转换。

首先是结构体对象和map转换成JSON字符串,使用json.Marshal这个函数:

func Marshal(v any) ([]byte, error)

这里的参数v,放置结构体对象本身,或者它的引用都可以。

得到的是[]byte类型的JSON,我们使用string()转换一下类型就得到JSON字符串了,代码如下所示:

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

func main() {
	person := Person{
		Name: "Alice",
		Age:  30,
	}
	dataMap := map[string]interface{}{
		"name": "Bob",
		"age":  25,
	}
	personJSONByte, _ := json.Marshal(person)
	personJSONStr := string(personJSONByte)
	mapJSONByte, _ := json.Marshal(dataMap)
	mapJsonStr := string(mapJSONByte)
}

反过来,JSON字符串转换成结构体对象和map,就要使用到json.Unmarshal函数:

func Unmarshal(data []byte, v any) error

第一个参数是[]byte类型的JSON,第二个参数必须是结构体对象或者map指针,代码如下所示:

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

func main() {
	jsonStr := `{"name":"Alice","age":30}`
	var person Person
	_ = json.Unmarshal([]byte(jsonStr), &person)
	var dataMap map[string]interface{}
	_ = json.Unmarshal([]byte(jsonStr), &dataMap)
}

结构体对象与JSON互转、mapJSON互转都是很方便的,但是结构体对象与map互转就有些麻烦了。

首先我们看结构体对象转map[string]interface{},有下面这样一个函数:

func structToMap(obj interface{}) map[string]interface{} {
	objMap := make(map[string]interface{})
	val := reflect.ValueOf(obj)
	typ := reflect.TypeOf(obj)
	for i := 0; i < val.NumField(); i++ {
		field := typ.Field(i)
		if field.PkgPath != "" && !field.Anonymous {
			continue
		}
		objMap[field.Name] = val.Field(i).Interface()
	}
	return objMap
}

利用了 Go的反射机制,动态地获取了结构体对象的字段和值,并转换为map[string]interface{}。不过,这并不是最佳解决方案,因为这需要我们手动去写反射的代码,并且在每个项目模块中都需要对其进行引入。

有一个第三方包很好地帮我们处理了这个问题,它就是github.com/fatih/structs

go get -u github.com/fatih/structs

它的核心方法就是Map函数,函数签名如下:

func Map(s interface{}) map[string]interface{}

它的入参是结构体对象,出参是map[string]interface{}。这里入参可以是对象本身,也可以是对象的引用。

这里需要注意的一点是,默认情况下,转换后的map[string]interface{}key值与结构体字段名完全一致,即首字母大写。如果想让mapkey值和结构体字段名构成映射,需要structs标签,这里在标签里设置omitempty可以让结构体的空值字段不写入到map

还需要注意,结构体的私有字段会被忽略。

使用示例如下所示:

type Person struct {
	Name    string `structs:"name,omitempty"`
	Age     int    `structs:"age,omitempty"`
	// 私有字段不会被转换到 map
	city    string
	Country string `structs:"country,omitempty"`
}

func main() {
	person := &Person{
		Name:    "Alice",
		Age:     30,
		city:    "New York",
		Country: "USA",
	}
	personMap := structs.Map(person)
	fmt.Println(personMap)  // map[age:30 country:USA name:Alice]
}

还有两个函数,一个用于获取所有字段名,一个用于获取所有字段值,函数签名如下:

func Names(s interface{}) []string
func Values(s interface{}) []interface{}

接下来就是map[string]interface{}转结构体对象,就更加麻烦了。map的值是任意类型,因此需要确保转换时类型匹配,否则会导致运行时错误,我们要对每个字段进行类型断言。

type Person struct {
	Name string
	Age  int
}

func main() {
	dataMap := map[string]interface{}{
        "name": "Alice",
        "age":  30,
    }
	person := mapToStruct(dataMap)
}

func mapToStruct(dataMap map[string]interface{}) Person {
	person := Person{}
	if name, ok := dataMap["name"].(string); ok {
		person.Name = name
	}
	if age, ok := dataMap["age"].(int); ok {
		person.Age = age
	}
	return person
}

非常难写且不通用,如果字段一多,代码量会是难以控制的,如果有嵌套字段,又会增加代码的复杂度。

有一个第三方包可以解决这个问题,就是github.com/mitchellh/mapstructure

go get -u github.com/mitchellh/mapstructure

这里我们使用Decode函数解决这个问题,它的函数签名如下:

func Decode(input interface{}, output interface{}) error

其中参数input是我们指定的map[string]interface{}output是要转换的结构体对象。使用示例如下:

type Person struct {
	Name string `mapstructure:"name"`
	Age  int    `mapstructure:"age"`
	City string `mapstructure:"city"`
}

func main() {
	data := map[string]interface{}{
		"name": "Alice",
		"age":  30,
		"city": "New York",
	}
	person := &Person{}
	_ = mapstructure.Decode(data, person)
}

这里需要注意的是output参数必须为已初始化的结构体对象的指针,否则会报错。还有一点就是这里的结构体标签是mapstructure,用来和map[string]interface{}key进行映射。

Decode函数也可以处理嵌套结构体,示例如下:

type Address struct {
	City    string `mapstructure:"city"`
	Country string `mapstructure:"country"`
}

type Person struct {
	Name    string  `mapstructure:"name"`
	Age     int     `mapstructure:"age"`
	Address Address `mapstructure:"address"`
}

func main() {
	data := map[string]interface{}{
		"name": "Alice",
		"age":  30,
		"address": map[string]interface{}{
			"city":    "New York",
			"country": "USA",
		},
	}
	person := &Person{}
	_ = mapstructure.Decode(data, person)
}

到这里,三种数据的互相转换就讲解完毕了。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将 Go 代码转换为 Scala 代码,并不是一件直接的事情,因为 Go 和 Scala 是两种不同的编程语言,它们有不同的语法和语义规则。但是,你可以通过手动重写 Go 代码来实现类似的功能。 以下是一些指导步骤,可以帮助你将 Go 代码转换为 Scala 代码: 1. 理解 Go 代码:首先,你需要仔细阅读和理解 Go 代码的功能和逻辑。这将帮助你在转换过程中更好地理解代码的要求和目标。 2. 熟悉 Scala 语法:了解 Scala 的语法和特性,包括函数、类、模式匹配等。这样你才能够正确地将 Go 代码转换为 Scala 代码。 3. 创建 Scala 项目:在 Scala 中创建一个新的项目,并设置好相应的构建工具和依赖项。 4. 逐行转换代码:根据 Go 代码的功能,逐行将其转换为 Scala 代码。这可能涉及到重写函数、变量和数据结构等。确保在转换过程中保持程序的逻辑正确性。 5. 调试和测试:在完成代码转换后,进行调试和测试以确保转换后的代码功能正确。 需要注意的是,由于 Go 和 Scala 是两种不同的语言,所以在转换过程中可能会遇到一些挑战和问题。某些功能可能无法直接转换,或者存在语义上的差异。因此,这个转换过程可能需要耗费一些时间和精力。 此外,还可以考虑使用其他的语言转换工具或库来辅助转换过程。例如,可以使用 ANTLR、ScalaPB 等工具来自动转换一部分代码。但是,这些工具只能提供一部分的转换结果,还需要手动进行进一步的修改和调整。 总而言之,将 Go 代码转换为 Scala 代码是一个复杂的任务,需要深入理解两种语言的特点和语法规则。希望以上的步骤能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值