首页 > 其他 > 详细

Golang结构体值的交换

时间:2019-12-11 16:29:29      阅读:137      评论:0      收藏:0      [点我收藏+]

Golang结构体值的交换

一、最初的做法

最先遇到这个问题是在比编写PUT方法的接口时遇到。
解决方法是增加一个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
    }
        ......
}

二、升级版本

最近看了波罗学大佬的一篇文章,突发奇想,可以使用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 内部结构不确定的情况

Golang结构体值的交换

原文:https://www.cnblogs.com/clod777/p/12022654.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!