golang工具篇-结构体合并切片

2023-10-17 11:14:15

主要应用场景:
将多个结构根据字段或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

  • 作者:他笑他自己
  • 原文链接:https://blog.csdn.net/weixin_37736913/article/details/127686320
    更新时间:2023-10-17 11:14:15