go reflect学习

需求: mongo 中的结构,可能会持续进行变更(新增成员) 对外的接口,已经提供出去,期望接口不变,能对数据进行更新。

这个时候可以利用 mongo 的update 和 reflect 完成需求。

import (
    "errors"
    "reflect"
    "strings"
)

var ErrInvalide = errors.New("invalid err")

func extractReflectValueMap(modifyValue reflect.Value, rMap map[string]interface{}) (err error) {
    if !modifyValue.IsValid() || modifyValue.Kind() != reflect.Struct {
        return ErrInvalide
    }

    for i := 0; i < modifyValue.NumField(); i++ {
        tag := modifyValue.Type().Field(i).Tag.Get("json")
        tag = strings.TrimSuffix(tag, ",omitempty")
        if _, ok := rMap[tag]; ok {
            return ErrInvalide
        }
        switch modifyValue.Field(i).Kind() {
        case reflect.Struct:
            sMap := make(map[string]interface{})
            err = extractReflectValueMap(modifyValue.Field(i), sMap)
            if err != nil {
                return
            }
            if tag != "" {
                rMap[tag] = sMap
            } else {
                for k, v := range sMap {
                    if _, ok := rMap[k]; ok {
                        return ErrInvalide
                    }
                    rMap[k] = v
                }
            }
        default:
            if tag == "" {
                return ErrInvalide
            }
            rMap[tag] = modifyValue.Field(i).Interface()
        }
    }
    return
}

type ApiData struct {
    Name string `json:"name,omitempty" bson:"name"`
    Age int `json:"age,omitempty" bson:"age"`
}

type YourDB struct {
    ApiData
    AppendMember string `json:"appendMember,omitempty" bson:"appendMember"`
}

func ApiModifyData(data *ApiData) {
    dealMap := make(map[string]interface{})
    if err = extractReflectValueMap(reflect.ValueOf(*data), dealMap); err != nil {
        return
    }

    m := bson.M{}
    for k, v := range dealMap {
        m[k] = v
    }

    yourMongo.update(bson.M{"key": key}, bson.M{"$set": m})
    return
}

伪代码的过程比较简单。 其他还可以补充,数据判断,异常处理等。