Skip to content

Commit d2a0aa1

Browse files
authored
Merge pull request #43 from devfeel/feature/generic-mapper
feat: 添加泛型Mapper支持
2 parents 347b0c0 + 42af14a commit d2a0aa1

File tree

3 files changed

+269
-0
lines changed

3 files changed

+269
-0
lines changed

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/devfeel/mapper
2+
3+
go 1.22.2

mapper_generic.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package mapper
2+
3+
import (
4+
"errors"
5+
"reflect"
6+
)
7+
8+
// ============ 泛型全局函数 (推荐使用) ============
9+
10+
// Map 同构类型映射 (泛型)
11+
// 使用示例: mapper.Map(&User{}, &User{})
12+
func Map[From any, To any](from *From, to *To) error {
13+
if from == nil || to == nil {
14+
return errors.New("from or to is nil")
15+
}
16+
return standardMapper.Mapper(from, to)
17+
}
18+
19+
// MapTo 异构类型映射 (泛型)
20+
// 使用示例: mapper.MapTo[Target](from, &target)
21+
func MapTo[To any](from any, to *To) error {
22+
if from == nil || to == nil {
23+
return errors.New("from or to is nil")
24+
}
25+
return standardMapper.Mapper(from, to)
26+
}
27+
28+
// MapSliceGeneric 泛型 Slice 映射
29+
// 使用示例: mapper.MapSliceGeneric(users, &targets)
30+
func MapSliceGeneric[From any, To any](fromSlice []From, toSlice *[]To) error {
31+
if fromSlice == nil || toSlice == nil {
32+
return errors.New("fromSlice or toSlice is nil")
33+
}
34+
35+
result := make([]To, len(fromSlice))
36+
for i, v := range fromSlice {
37+
// 创建 From 指针
38+
fromPtr := reflect.New(reflect.TypeOf(v)).Interface()
39+
reflect.ValueOf(fromPtr).Elem().Set(reflect.ValueOf(v))
40+
41+
var target To
42+
err := standardMapper.Mapper(fromPtr, &target)
43+
if err != nil {
44+
return err
45+
}
46+
result[i] = target
47+
}
48+
*toSlice = result
49+
return nil
50+
}
51+
52+
// MapToSliceGeneric 泛型 Map 转 Slice
53+
// 使用示例: mapper.MapToSliceGeneric[Target](mapData, &targets)
54+
func MapToSliceGeneric[T any](fromMap map[string]any, toSlice *[]T) error {
55+
if fromMap == nil || toSlice == nil {
56+
return errors.New("fromMap or toSlice is nil")
57+
}
58+
59+
// 创建 T 类型的空切片
60+
result := make([]T, 0)
61+
62+
for _, v := range fromMap {
63+
if data, ok := v.(map[string]any); ok {
64+
var target T
65+
err := standardMapper.MapperMap(data, &target)
66+
if err != nil {
67+
return err
68+
}
69+
result = append(result, target)
70+
}
71+
}
72+
*toSlice = result
73+
return nil
74+
}
75+
76+
// ============ 泛型 Mapper 实例 (可选) ============
77+
78+
// MapperGeneric 泛型 Mapper 简化实例
79+
// 内部组合标准 mapper,复用反射逻辑
80+
type MapperGeneric struct {
81+
mapper IMapper
82+
}
83+
84+
// NewMapperGeneric 创建泛型 Mapper 实例
85+
func NewMapperGeneric() *MapperGeneric {
86+
return &MapperGeneric{
87+
mapper: standardMapper,
88+
}
89+
}
90+
91+
// Map 同构类型映射
92+
func (m *MapperGeneric) Map(from, to any) error {
93+
if from == nil || to == nil {
94+
return errors.New("from or to is nil")
95+
}
96+
return m.mapper.Mapper(from, to)
97+
}
98+
99+
// MapTo 异构类型映射 - 泛型方法
100+
// 注意: Go 接口方法不支持泛型,因此这里使用 any 再内部转换
101+
func (m *MapperGeneric) MapTo(to any, from any) error {
102+
if from == nil || to == nil {
103+
return errors.New("from or to is nil")
104+
}
105+
return m.mapper.Mapper(from, to)
106+
}
107+
108+
// MapSlice 泛型 Slice 映射
109+
func (m *MapperGeneric) MapSlice(fromSlice, toSlice any) error {
110+
if fromSlice == nil || toSlice == nil {
111+
return errors.New("fromSlice or toSlice is nil")
112+
}
113+
return m.mapper.MapperSlice(fromSlice, toSlice)
114+
}
115+
116+
// MapperGenericInstance 全局泛型 Mapper 实例
117+
var MapperGenericInstance = NewMapperGeneric()

mapper_generic_test.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package mapper
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// 测试类型定义
8+
type (
9+
SourceUser struct {
10+
Name string
11+
Age int
12+
}
13+
14+
TargetUser struct {
15+
Name string
16+
Age int
17+
}
18+
19+
SourceWithTag struct {
20+
UserName string `mapper:"name"`
21+
UserAge int `mapper:"age"`
22+
}
23+
24+
TargetWithTag struct {
25+
Name string `mapper:"name"`
26+
Age int `mapper:"age"`
27+
}
28+
)
29+
30+
func Test_Generic_Map(t *testing.T) {
31+
from := &SourceUser{Name: "test", Age: 25}
32+
to := &TargetUser{}
33+
34+
err := Map(from, to)
35+
if err != nil {
36+
t.Error("Map failed:", err)
37+
}
38+
39+
if to.Name != from.Name || to.Age != from.Age {
40+
t.Error("Map result not match", to, from)
41+
} else {
42+
t.Log("Map success:", to)
43+
}
44+
}
45+
46+
func Test_Generic_MapTo(t *testing.T) {
47+
from := &SourceWithTag{UserName: "test", UserAge: 25}
48+
to := &TargetWithTag{}
49+
50+
err := MapTo(from, to)
51+
if err != nil {
52+
t.Error("MapTo failed:", err)
53+
}
54+
55+
if to.Name != from.UserName || to.Age != from.UserAge {
56+
t.Error("MapTo result not match", to, from)
57+
} else {
58+
t.Log("MapTo success:", to)
59+
}
60+
}
61+
62+
func Test_Generic_MapSlice(t *testing.T) {
63+
fromSlice := []SourceUser{
64+
{Name: "user1", Age: 10},
65+
{Name: "user2", Age: 20},
66+
{Name: "user3", Age: 30},
67+
}
68+
69+
var toSlice []TargetUser
70+
71+
err := MapSliceGeneric(fromSlice, &toSlice)
72+
if err != nil {
73+
t.Error("MapSliceGeneric failed:", err)
74+
}
75+
76+
if len(toSlice) != 3 {
77+
t.Error("MapSliceGeneric length not match")
78+
} else {
79+
t.Log("MapSliceGeneric success:", toSlice)
80+
}
81+
}
82+
83+
func Test_Generic_MapToSlice(t *testing.T) {
84+
fromMap := map[string]any{
85+
"user1": map[string]any{"Name": "user1", "Age": 10},
86+
"user2": map[string]any{"Name": "user2", "Age": 20},
87+
}
88+
89+
var toSlice []TargetUser
90+
91+
err := MapToSliceGeneric(fromMap, &toSlice)
92+
if err != nil {
93+
t.Error("MapToSliceGeneric failed:", err)
94+
}
95+
96+
if len(toSlice) != 2 {
97+
t.Error("MapToSliceGeneric length not match")
98+
} else {
99+
t.Log("MapToSliceGeneric success:", toSlice)
100+
}
101+
}
102+
103+
func Benchmark_Generic_Map(b *testing.B) {
104+
from := &SourceUser{Name: "test", Age: 25}
105+
to := &TargetUser{}
106+
107+
b.ResetTimer()
108+
for i := 0; i < b.N; i++ {
109+
Map(from, to)
110+
}
111+
}
112+
113+
func Benchmark_Traditional_Map(b *testing.B) {
114+
from := &SourceUser{Name: "test", Age: 25}
115+
to := &TargetUser{}
116+
117+
b.ResetTimer()
118+
for i := 0; i < b.N; i++ {
119+
Mapper(from, to)
120+
}
121+
}
122+
123+
func Benchmark_Generic_MapSlice(b *testing.B) {
124+
fromSlice := make([]SourceUser, 100)
125+
for i := 0; i < 100; i++ {
126+
fromSlice[i] = SourceUser{Name: "user", Age: i}
127+
}
128+
129+
var toSlice []TargetUser
130+
131+
b.ResetTimer()
132+
for i := 0; i < b.N; i++ {
133+
MapSliceGeneric(fromSlice, &toSlice)
134+
}
135+
}
136+
137+
func Benchmark_Traditional_MapperSlice(b *testing.B) {
138+
fromSlice := make([]SourceUser, 100)
139+
for i := 0; i < 100; i++ {
140+
fromSlice[i] = SourceUser{Name: "user", Age: i}
141+
}
142+
143+
toSlice := make([]TargetUser, 100)
144+
145+
b.ResetTimer()
146+
for i := 0; i < b.N; i++ {
147+
MapperSlice(fromSlice, toSlice)
148+
}
149+
}

0 commit comments

Comments
 (0)