go convert_struct_to_map && convert_map_to_struct(reflect)

下午补业务系统的单测时,发现 util 包中 ConvertStructToMapConvertMapToStruct 两个函数写的有点问题,于是简单地实现了最基本的转化功能。主要基于 golangreflect 包进行相应操作。顺带撸了下 github 中两个基于 reflect 进行 mapstruct 操作的开源库。

简单实现了两个函数的基本操作,至于一些扩展功能,在 TODO 处添加相应的处理代码即可:

package main

import (
	"fmt"
	"reflect"
	"strings"
)

const (
	// DefaultTagName 默认 tag_name
	DefaultTagName = "json"
)

// Person 代表 person
type Person struct {
	Age    int    `json:"age"`
	Name   string `json:"name"`
	Offer  string `json:"-"`
	Gender string
}

// "age,omitempty"
func parseTags(s string) (string, []string) {

	if len(s) == 0 {
		return "", nil
	}

	sl := strings.Split(s, ",")
	return sl[0], sl[1:]
}

// ConvertStructToMap struct to map json tag
func ConvertStructToMap(val interface{}) (map[string]interface{}, error) {

	var (
		rv     = reflect.ValueOf(val)
		rvt    = rv.Type()
		fields []reflect.StructField
		result = map[string]interface{}{}
	)

	for rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
	}
	if rv.Kind() != reflect.Struct {
		return nil, fmt.Errorf("not struct")
	}

	// 2. get all struct_field
	for i := 0; i < rvt.NumField(); i++ {

		field := rvt.Field(i)

		// unexported field
		if field.PkgPath != "" {
			continue
		}

		// ignored tag
		if tag := field.Tag.Get(DefaultTagName); tag == "-" {
			continue
		}

		fields = append(fields, field)
	}

	// 3. fill map
	for _, field := range fields {

		fieldName := field.Name
		fieldValue := rv.FieldByName(fieldName)

		// tagName, tagOpts
		tagName, _ := parseTags(field.Tag.Get(DefaultTagName))

		// TODO: add some json tag option process

		if len(tagName) == 0 {
			tagName = fieldName
		}
		result[tagName] = fieldValue.Interface()
	}
	return result, nil
}

// SetField struct field set value
func SetField(sval interface{}, name string, val interface{}) error {

	var (
		sv = reflect.ValueOf(sval)
		fv reflect.Value
		ft reflect.Type
	)

	for sv.Kind() == reflect.Ptr {
		sv = sv.Elem()
	}

	// TODO: tag --> field_name
	// TODO: sv.FieldByNameFunc
	fv = sv.FieldByName(name)
	if !fv.IsValid() {
		return fmt.Errorf("No such field: %s in obj", name)
	}
	if !fv.CanSet() {
		return fmt.Errorf("Cannot set %s field value", name)
	}

	ft = fv.Type()
	if ft != reflect.ValueOf(val).Type() {
		return fmt.Errorf("Provided value type didn't match obj field type")
	}

	fv.Set(reflect.ValueOf(val))

	return nil
}

// ConvertMapToStruct params map fill struct
func ConvertMapToStruct(params map[string]interface{}, val interface{}) {
	for field, fieldVal := range params {
		if err := SetField(val, field, fieldVal); err != nil {
			fmt.Println(err)
		}
	}
}

func main() {
	var p = Person{
		Age:    18,
		Name:   "wangxiyang",
		Offer:  "didi",
		Gender: "nan",
	}
	fmt.Println(ConvertStructToMap(p))

	var p2 Person
	var params = map[string]interface{}{
		"Age":    18,
		"Name":   "wangxiyang",
		"Offer":  "didi",
		"Gender": "nan",
	}
	// 必须传指针方能 can_set
	ConvertMapToStruct(params, &p2)
	fmt.Println(p2)
}

猜你喜欢

转载自blog.csdn.net/u011728372/article/details/86497839
今日推荐