主要应用场景:
将多个结构根据字段或Tag名称导出合并到 目的结构体切片中,并可以根据MERGE_MODEL类型选择不同的导出合并模式。
package utilimport("fmt""reflect""strings")type MERGE_MODELstringconst(
FIELDMODEL MERGE_MODEL="FIELDMODEL"//字段名
TAGMODEL MERGE_MODEL="TAGMODEL"//匹配标签名
TAGORFIELDMODEL MERGE_MODEL="TAGORFIELDMODEL"//优先匹配Tag)type StructUtilstruct{
toleratebool
mergeModel MERGE_MODEL}type Optionsstruct{
toleratebool
mergeModel MERGE_MODEL}type OptionFuncfunc(*Options)funcWithMergeModel(mergeModel MERGE_MODEL) OptionFunc{returnfunc(o*Options){
o.mergeModel= mergeModel}}funcWithTolerate(toleratebool) OptionFunc{returnfunc(o*Options){
o.tolerate= tolerate}}funcNewStructUtil(handlers...OptionFunc)*StructUtil{
options:=&Options{
tolerate:true,
mergeModel: FIELDMODEL,}for_, o:=range handlers{o(options)}return&StructUtil{
tolerate: options.tolerate,
mergeModel: options.mergeModel,}}func MultiStructMergeToSlice[T any](util*StructUtil, data...interface{})(res[]T, errerror){
res=make([]T,0,len(data))for index, val:=range data{
rfv:= reflect.ValueOf(val)if!rfv.IsValid()&&!util.tolerate{returnnil, fmt.Errorf("data[%v] is invalid ", index)}
rfk:= rfv.Kind()
rft:= rfv.Type()if rfk== reflect.Ptr|| rfk== reflect.Interface{
rfv= rfv.Elem()
rft= rft.Elem()
rfk= rfv.Kind()}if rfk== reflect.Struct{
genericVal:=new(T)
gRfv:= reflect.ValueOf(genericVal).Elem()
gRft:= gRfv.Type()
gvFieldNums:= gRfv.NumField()
fieldNums:= rfv.NumField()if util.mergeModel== FIELDMODEL{for i:=0; i< gvFieldNums&& gRfv.Field(i).CanSet(); i++{
gRftFieldName:= gRft.Field(i).Name
gRftType:= gRft.Field(i).Typefor j:=0; j< fieldNums; j++{if rft.Field(j).Name== gRftFieldName&&
rft.Field(j).Type== gRftType{
gRfv.Field(i).Set(rfv.Field(j))break}}}}elseif util.mergeModel== TAGORFIELDMODEL{for i:=0; i< gvFieldNums&& gRfv.Field(i).CanSet(); i++{
gRftPattern:= gRft.Field(i).Name
gRftType:= gRft.Field(i).Type
gTags:= strings.Split(string(gRft.Field(i).Tag),"\"")iflen(gTags)>1&& gTags[1]!="-"{
gRftPattern= gTags[1]}for j:=0; j< fieldNums; j++{var rftPatternstring
tags:= strings.Split(string(rft.Field(j).Tag),"\"")iflen(tags)>1{
rftPattern= tags[1]}if(rftPattern== gRftPattern)&&
rft.Field(j).Type== gRftType{
gRfv.Field(i).Set(rfv.Field(j))break}}}}elseif util.mergeModel== TAGMODEL{for i:=0; i< gvFieldNums&& gRfv.Field(i).CanSet(); i++{
gRftType:= gRft.Field(i).Type
gTags:= strings.Split(string(gRft.Field(i).Tag),"\"")iflen(gTags)>1&& gTags[1]!="-"{
gRftTagName:= gTags[1]for j:=0; j< fieldNums; j++{var rftTagNamestring
tags:= strings.Split(string(rft.Field(j).Tag),"\"")iflen(tags)>1{
rftTagName= tags[1]}if(rftTagName== gRftTagName)&&
rft.Field(j).Type== gRftType{
gRfv.Field(i).Set(rfv.Field(j))break}}}}}
res=append(res,*genericVal)}elseif!util.tolerate{returnnil, fmt.Errorf("data[%v] kind is %v", index, rfk)}}return res,nil}
测试
package utilimport("fmt""testing")type Userstruct{
Iduint`json:"_id"`
Namestring`json:"name"`
Sexstring`json:"sex"`
Heightint`json:"height"`}type User1struct{
Iduint`json:"_id"`
Namestring`json:"name"`
Sexstring`json:"sex"`}type User2struct{
Iduint`json:"id"`
Namestring`json:"name"`
Heightint`json:"height"`}funcTestMultiStructMergeToSlice(t*testing.T){
structUtil:=NewStructUtil()//structUtil := NewStructUtil(WithMergeModel(TAGORFIELDMODEL))//structUtil := NewStructUtil(WithMergeModel(TAGMODEL))
user1:=&User1{
Id:1,
Name:"user1",
Sex:"男",}
user2:= User2{
Id:1,
Name:"user1",
Height:175,}var data[]interface{}
data=append(data, user1, user2)type argsstruct{
util*StructUtil
data[]interface{}}
tests:=[]struct{
namestring
args args
wantErrbool}{// TODO: Add test cases.{
name:"test1",
args: args{
util: structUtil,
data: data,},
wantErr:false,},}for_, tt:=range tests{
t.Run(tt.name,func(t*testing.T){if res, err:= MultiStructMergeToSlice[User](tt.args.util, tt.args.data...);(err!=nil)!= tt.wantErr{
t.Errorf("MultiStructMergeToSlice() error = %v, wantErr %v", err, tt.wantErr)}else{
fmt.Println("res:", res)}})}}
效果预览:
=== RUN TestMultiStructMergeToSlice
=== RUN TestMultiStructMergeToSlice/test1
res: [{1 user1 男 0} {1 user1 175}]
— PASS: TestMultiStructMergeToSlice (0.00s)
— PASS: TestMultiStructMergeToSlice/test1 (0.00s)
PASS