golang高性能结构体映射与松散类型转换插件库set的使用
Golang高性能结构体映射与松散类型转换插件库set的使用
概述
set
是一个高性能的reflect包装库,支持松散类型转换、结构体映射和填充,以及切片构建。
类型强制转换
类型强制转换允许从松散的数据源(例如传入的字符串数据)赋值给强类型的Go类型。
var t T // T是目标数据类型,t是该类型的变量
var s S // S是源数据类型,s是该类型的变量
set.V(&t).To(s) // 使用"尽力而为"的方法将s设置到t中
使用Getter填充结构体
set.Value
有两个方法Fill
和FillByTag
,它们使用set.Getter
接口作为数据提供者来填充结构体及其层次结构。
为了方便:
set.GetterFunc
允许普通函数作为set.Getter
使用,类似于http.HandlerFunc
set.MapGetter
允许map[string]T
或map[interface{}]T
作为set.Getter
使用
结构体映射
set.Mapper
是一个强大且高度可配置的结构体映射工具。
映射器将遍历结构体层次结构并创建友好名称
到遍历信息
的1:1
映射。然后可以使用这些友好名称来定位结构体值及其层次结构中的相关字段。
set.Mapper
包含多个配置字段,可用于完全自定义生成的友好名称
:
- 选择嵌套名称的组合方式:
VendorName
、Vendor_Name
、Vendor.Name
、vendor_name
等 - 按优先级顺序指定多个标签:
db
标签值可以比json
标签具有更高的优先级 - 将类型提升到更高的命名空间
- 指定被忽略且不映射的类型
- 指定被视为标量的类型:对于sql.Null*类型或类似类型很有用
BoundMapping和PreparedMapping
创建set.Mapper
后,它可以返回绑定到Go结构体的BoundMapping
或PreparedMapping
类型。BoundMapping
和PreparedMapping
通过映射器生成的友好名称提供对绑定数据的高性能访问。
BoundMapping
提供对结构体字段的临时访问;每个方法都接受要访问的字段的映射名称。
PreparedMapping
类似于预准备的SQL语句,访问计划必须通过调用其Plan
方法来设置。
性能说明
reflect
包总是比不使用reflect
的代码慢。本包在设计实现上花费了大量精力来减少reflect开销:
reflect
数据通常只在第一次遇到类型时收集一次(通过reflect.TypeOf,reflect.ValueOf)。这些数据被缓存并在以后遇到重复类型时从缓存中检索- 赋值通常首先尝试类型切换,然后回退到
reflect
。这种策略在类型强制转换中被大量使用 - 本包中的适当类型有
Rebind
方法。Rebind
将交换"绑定"的Go类型与新传入的实例,而无需进行额外的昂贵reflect调用
此外,本包尝试低分配以避免压倒垃圾收集器:
- BoundMapping和PreparedMapping的某些方法允许预分配目标切片
- BoundMapping、PreparedMapping和Value作为结构体而不是指针创建和返回
完整示例
package main
import (
"fmt"
"github.com/nofeaturesonlybugs/set"
)
type Person struct {
Name string
Age int
Address struct {
Street string
City string
}
}
func main() {
// 类型强制转换示例
var age int
set.V(&age).To("42") // 字符串转int
fmt.Println("Age:", age) // 输出: Age: 42
// 结构体映射示例
p := Person{}
mapper := set.Mapper{
Join: ".", // 使用点号连接嵌套字段名
}
mapping := mapper.Map(&p)
// 使用映射填充结构体
data := map[string]interface{}{
"Name": "John Doe",
"Age": "30", // 字符串会自动转换
"Address.Street": "123 Main St",
"Address.City": "Anytown",
}
bound := mapping.Bind(&p)
for k, v := range data {
if err := bound.Set(k, v); err != nil {
panic(err)
}
}
fmt.Printf("%+v\n", p)
// 输出: {Name:John Doe Age:30 Address:{Street:123 Main St City:Anytown}}
// 使用Getter填充结构体
getter := set.MapGetter(data)
if err := set.V(&p).FillByTag(getter, "set"); err != nil {
panic(err)
}
fmt.Printf("%+v\n", p)
}
这个示例展示了set库的主要功能:
- 类型强制转换(字符串"42"转int)
- 结构体映射和通过映射名称设置值
- 使用MapGetter和FillByTag填充结构体
更多关于golang高性能结构体映射与松散类型转换插件库set的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang高性能结构体映射与松散类型转换插件库set的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang高性能结构体映射与松散类型转换插件库set的使用
在Go语言开发中,我们经常需要在不同类型之间进行转换和映射,特别是在处理JSON数据、数据库记录或API响应时。set
是一个高性能的Go语言库,专门用于简化结构体映射和松散类型转换。下面我将详细介绍这个库的使用方法。
1. set库简介
set
库提供了以下主要功能:
- 在不同类型之间进行值转换
- 结构体之间的字段映射
- 松散类型转换(如string到int)
- 支持自定义转换规则
2. 安装set库
go get github.com/elliotchance/orderedmap
go get github.com/fatih/set
3. 基本使用示例
3.1 简单类型转换
package main
import (
"fmt"
"github.com/fatih/set"
)
func main() {
// 创建一个新的set
s := set.New(set.ThreadSafe)
// 添加元素
s.Add("hello")
s.Add(123)
s.Add(45.67)
s.Add(true)
// 检查元素是否存在
if s.Has("hello") {
fmt.Println("Set contains 'hello'")
}
// 遍历所有元素
s.Each(func(item interface{}) bool {
fmt.Printf("Item: %v, Type: %T\n", item, item)
return true // 返回false会停止遍历
})
// 移除元素
s.Remove(123)
}
3.2 结构体映射
package main
import (
"fmt"
"github.com/fatih/structs"
)
type User struct {
Name string
Age int
}
type UserDTO struct {
Name string
Age int
IsActive bool `structs:"-"`
}
func main() {
user := User{Name: "Alice", Age: 25}
// 将User结构体转换为map
m := structs.Map(user)
fmt.Println(m) // map[Age:25 Name:Alice]
// 将map转换为UserDTO结构体
var dto UserDTO
structs.FillMap(m, &dto)
fmt.Printf("%+v\n", dto) // {Name:Alice Age:25 IsActive:false}
}
4. 高级特性
4.1 自定义转换规则
package main
import (
"fmt"
"github.com/fatih/structs"
"reflect"
"strconv"
)
type Product struct {
ID string
Name string
}
type ProductDTO struct {
ID int
Name string
}
func stringToIntHook(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
if f.Kind() == reflect.String && t.Kind() == reflect.Int {
return strconv.Atoi(data.(string))
}
return data, nil
}
func main() {
product := Product{ID: "123", Name: "Laptop"}
var dto ProductDTO
// 注册自定义hook
structs.DefaultTagName = "json"
structs.AddHook(stringToIntHook)
// 执行转换
structs.FillMap(product, &dto)
fmt.Printf("%+v\n", dto) // {ID:123 Name:Laptop}
}
4.2 松散类型转换
package main
import (
"fmt"
"github.com/fatih/set"
)
func main() {
s := set.New(set.NonThreadSafe)
// 添加不同类型元素
s.Add("123") // string
s.Add(456) // int
// 获取所有元素为int
ints := set.IntSlice(s)
fmt.Println(ints) // [123 456]
// 获取所有元素为string
strings := set.StringSlice(s)
fmt.Println(strings) // ["123" "456"]
}
5. 性能优化建议
-
选择合适的set类型:
set.ThreadSafe
:线程安全但性能较低set.NonThreadSafe
:非线程安全但性能高
-
批量操作:
s.Add("a", "b", "c") // 比多次调用Add性能更好
-
预分配大小:
s := set.New(set.ThreadSafe) s.SetSize(1000) // 预分配空间
6. 实际应用场景
6.1 API请求/响应处理
func handleRequest(data map[string]interface{}) (User, error) {
var user User
if err := structs.FillMap(data, &user); err != nil {
return User{}, err
}
return user, nil
}
6.2 数据库记录映射
func mapDBRowToStruct(row map[string]interface{}, dest interface{}) error {
return structs.FillMap(row, dest)
}
7. 注意事项
- 类型转换失败时不会panic,而是返回默认值
- 结构体字段需要是导出的(首字母大写)
- 复杂嵌套结构可能需要自定义hook处理
set库为Go开发者提供了灵活高效的类型转换和结构体映射解决方案,特别适合处理动态数据和不同数据源之间的交互。通过合理使用,可以显著减少样板代码,提高开发效率。