Golang结构体值的交换

Golang结构体值的交换

一、添加结构体,多if暴力

最先遇到这个问题是在比编写PUT方法的接口时遇到。
(我公司编写http put方法,是先解析json至StudentInput结构体中,通过if input.Name!=nil来判断是否需要修改原数据的值)
解决方法是增加一个Input struct,该struct字段的变量都为原结构体的指针类型。
优点:可以根据业务逻辑做出改变。
缺点:冗余、易错(每次测试PUT方法时,需要特别小心)。
代码如下:

type Student struct {
	ID   string `json:"id" 
	Name string `json:"name"
	Age  int    `json:"age"
}
type StudentInput struct {
	ID   *string `json:"id" `
	Name *string `json:"name"`
	Age  *int    `json:"age" `
}

func Handle() {
        var source Student
        var input StudentInput
        ......
	if input.Name != nil {
		source.Name = *input.Name
	}
        ......
}

这个做法是标准,只是我认为太繁琐想简化一下,参考这篇文章

二、设置tag,利用反射代替多if

最近看了波罗学大佬的一篇文章,突发奇想,可以使用Go的反射实现交换struct中的值。
类似的作品就想到了开源的mapstruct,参考了一下源码,写了一个劣质Demo。
代码如下:

func Swap(source, input interface{}) error {
	sourceVal := reflect.ValueOf(source)
	inputVal := reflect.ValueOf(input)

	if sourceVal.Kind() != reflect.Ptr {
		return errors.New("source must be a pointer")
	}

	if inputVal.Kind() != reflect.Struct {
		return errors.New("input must be a struct")
	}

	defer func() {
		if r := recover(); r != nil {
			log.Println(r)
			return
		}
	}()

	for i := 0; i < inputVal.NumField(); i++ {
		for j := 0; j < sourceVal.Elem().NumField(); j++ {
			// 零值处理
			if inputVal.Field(i).IsZero() {
				continue
			}

			// 比较tag
			if tagEqual(sourceVal.Type().Elem().Field(j), inputVal.Type().Field(i)) {
				sourceVal.Elem().Field(i).Set(inputVal.Field(j))
				break
			}
		}
	}

	return nil
}

func tagEqual(s1, s2 reflect.StructField) bool {
	return s1.Tag.Get("swapstruct") == s2.Tag.Get("swapstruct")
}

思路很简单,需要修改的字段在tag中增加swapstruct,运用反射查找相同tag修改值。
优点:简单,不容易出错。
缺点:性能较差。

测试代码如下:

type Student struct {
	ID   string `json:"id" swapstruct:"id"`
	Name string `json:"name" swapstruct:"name"`
	Age  int    `json:"age" swapstruct:"age"`
}
func main() {
	id := "inputID"
	name := "inputName"
	//age := 0

	input := Student{
		ID:   id,
		Name: name,
		//Age:  age,
	}

	source := Student{
		ID:   "sourceID",
		Name: "sourceName",
		Age:  22,
	}

	fmt.Println("source: ", source)
	fmt.Println("input: ", input)
	if err := Swap(&source, input); err != nil {
		log.Println(err)
	}
	fmt.Println("after: ", source)
}

三、参考

1.mapstructure: https://github.com/mitchellh/mapstructure
2.Go 如何解析 json 内部结构不确定的情况

posted @ 2019-12-11 15:08  clod7  阅读(872)  评论(0)    收藏  举报