|
- package utils
-
- import (
- "encoding/json"
- "fmt"
- "reflect"
- "strconv"
- "strings"
- )
-
- func Map2Struct(vals map[string]interface{}, dst interface{}) (err error) {
- return Map2StructByTag(vals, dst, "json")
- }
-
- func Map2StructByTag(vals map[string]interface{}, dst interface{}, structTag string) (err error) {
- defer func() {
- e := recover()
- if e != nil {
- if v, ok := e.(error); ok {
- err = fmt.Errorf("Panic: %v", v.Error())
- } else {
- err = fmt.Errorf("Panic: %v", e)
- }
- }
- }()
-
- pt := reflect.TypeOf(dst)
- pv := reflect.ValueOf(dst)
-
- if pv.Kind() != reflect.Ptr || pv.Elem().Kind() != reflect.Struct {
- return fmt.Errorf("not a pointer of struct")
- }
-
- var f reflect.StructField
- var ft reflect.Type
- var fv reflect.Value
-
- for i := 0; i < pt.Elem().NumField(); i++ {
- f = pt.Elem().Field(i)
- fv = pv.Elem().Field(i)
- ft = f.Type
-
- if f.Anonymous || !fv.CanSet() {
- continue
- }
-
- tag := f.Tag.Get(structTag)
-
- name, option := parseTag(tag)
-
- if name == "-" {
- continue
- }
-
- if name == "" {
- name = strings.ToLower(f.Name)
- }
- val, ok := vals[name]
-
- if !ok {
- if option == "required" {
- return fmt.Errorf("'%v' not found", name)
- }
- if len(option) != 0 {
- val = option // default value
- } else {
- //fv.Set(reflect.Zero(ft)) // TODO set zero value or just ignore it?
- continue
- }
- }
-
- // convert or set value to field
- vv := reflect.ValueOf(val)
- vt := reflect.TypeOf(val)
-
- if vt.Kind() != reflect.String {
- // try to assign and convert
- if vt.AssignableTo(ft) {
- fv.Set(vv)
- continue
- }
-
- if vt.ConvertibleTo(ft) {
- fv.Set(vv.Convert(ft))
- continue
- }
-
- return fmt.Errorf("value type not match: field=%v(%v) value=%v(%v)", f.Name, ft.Kind(), val, vt.Kind())
- }
- s := strings.TrimSpace(vv.String())
- if len(s) == 0 && option == "required" {
- return fmt.Errorf("value of required argument can't not be empty")
- }
- fk := ft.Kind()
-
- // convert string to value
- if fk == reflect.Ptr && ft.Elem().Kind() == reflect.String {
- fv.Set(reflect.ValueOf(&s))
- continue
- }
- if fk == reflect.Ptr || fk == reflect.Struct {
- err = convertJsonValue(s, name, fv)
- } else if fk == reflect.Slice {
- err = convertSlice(s, f.Name, ft, fv)
- } else {
- err = convertValue(fk, s, f.Name, fv)
- }
-
- if err != nil {
- return err
- }
- continue
- }
-
- return nil
- }
-
- func Struct2Map(s interface{}) map[string]interface{} {
- return Struct2MapByTag(s, "json")
- }
- func Struct2MapByTag(s interface{}, tagName string) map[string]interface{} {
- t := reflect.TypeOf(s)
- v := reflect.ValueOf(s)
-
- if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
- t = t.Elem()
- v = v.Elem()
- }
-
- if v.Kind() != reflect.Struct {
- return nil
- }
-
- m := make(map[string]interface{})
-
- for i := 0; i < t.NumField(); i++ {
- fv := v.Field(i)
- ft := t.Field(i)
-
- if !fv.CanInterface() {
- continue
- }
-
- if ft.PkgPath != "" { // unexported
- continue
- }
-
- var name string
- var option string
- tag := ft.Tag.Get(tagName)
- if tag != "" {
- ts := strings.Split(tag, ",")
- if len(ts) == 1 {
- name = ts[0]
- } else if len(ts) > 1 {
- name = ts[0]
- option = ts[1]
- }
- if name == "-" {
- continue // skip this field
- }
- if name == "" {
- name = strings.ToLower(ft.Name)
- }
- if option == "omitempty" {
- if isEmpty(&fv) {
- continue // skip empty field
- }
- }
- } else {
- name = strings.ToLower(ft.Name)
- }
-
- if ft.Anonymous && fv.Kind() == reflect.Ptr && fv.IsNil() {
- continue
- }
- if (ft.Anonymous && fv.Kind() == reflect.Struct) ||
- (ft.Anonymous && fv.Kind() == reflect.Ptr && fv.Elem().Kind() == reflect.Struct) {
-
- // embedded struct
- embedded := Struct2MapByTag(fv.Interface(), tagName)
- for embName, embValue := range embedded {
- m[embName] = embValue
- }
- } else if option == "string" {
- kind := fv.Kind()
- if kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64 {
- m[name] = strconv.FormatInt(fv.Int(), 10)
- } else if kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64 {
- m[name] = strconv.FormatUint(fv.Uint(), 10)
- } else if kind == reflect.Float32 || kind == reflect.Float64 {
- m[name] = strconv.FormatFloat(fv.Float(), 'f', 2, 64)
- } else {
- m[name] = fv.Interface()
- }
- } else {
- m[name] = fv.Interface()
- }
- }
-
- return m
- }
-
- func isEmpty(v *reflect.Value) bool {
- k := v.Kind()
- if k == reflect.Bool {
- return v.Bool() == false
- } else if reflect.Int < k && k < reflect.Int64 {
- return v.Int() == 0
- } else if reflect.Uint < k && k < reflect.Uintptr {
- return v.Uint() == 0
- } else if k == reflect.Float32 || k == reflect.Float64 {
- return v.Float() == 0
- } else if k == reflect.Array || k == reflect.Map || k == reflect.Slice || k == reflect.String {
- return v.Len() == 0
- } else if k == reflect.Interface || k == reflect.Ptr {
- return v.IsNil()
- }
- return false
- }
-
- func convertSlice(s string, name string, ft reflect.Type, fv reflect.Value) error {
- var err error
- et := ft.Elem()
-
- if et.Kind() == reflect.Ptr || et.Kind() == reflect.Struct {
- return convertJsonValue(s, name, fv)
- }
-
- ss := strings.Split(s, ",")
-
- if len(s) == 0 || len(ss) == 0 {
- return nil
- }
-
- fs := reflect.MakeSlice(ft, 0, len(ss))
-
- for _, si := range ss {
- ev := reflect.New(et).Elem()
-
- err = convertValue(et.Kind(), si, name, ev)
- if err != nil {
- return err
- }
- fs = reflect.Append(fs, ev)
- }
-
- fv.Set(fs)
-
- return nil
- }
-
- func convertJsonValue(s string, name string, fv reflect.Value) error {
- var err error
- d := StringToSlice(s)
-
- if fv.Kind() == reflect.Ptr {
- if fv.IsNil() {
- fv.Set(reflect.New(fv.Type().Elem()))
- }
- } else {
- fv = fv.Addr()
- }
-
- err = json.Unmarshal(d, fv.Interface())
-
- if err != nil {
- return fmt.Errorf("invalid json '%v': %v, %v", name, err.Error(), s)
- }
-
- return nil
- }
-
- func convertValue(kind reflect.Kind, s string, name string, fv reflect.Value) error {
- if !fv.CanAddr() {
- return fmt.Errorf("can not addr: %v", name)
- }
-
- if kind == reflect.String {
- fv.SetString(s)
- return nil
- }
-
- if kind == reflect.Bool {
- switch s {
- case "true":
- fv.SetBool(true)
- case "false":
- fv.SetBool(false)
- case "1":
- fv.SetBool(true)
- case "0":
- fv.SetBool(false)
- default:
- return fmt.Errorf("invalid bool: %v value=%v", name, s)
- }
- return nil
- }
-
- if reflect.Int <= kind && kind <= reflect.Int64 {
- i, err := strconv.ParseInt(s, 10, 64)
- if err != nil {
- return fmt.Errorf("invalid int: %v value=%v", name, s)
- }
- fv.SetInt(i)
-
- } else if reflect.Uint <= kind && kind <= reflect.Uint64 {
- i, err := strconv.ParseUint(s, 10, 64)
- if err != nil {
- return fmt.Errorf("invalid int: %v value=%v", name, s)
- }
- fv.SetUint(i)
-
- } else if reflect.Float32 == kind || kind == reflect.Float64 {
- i, err := strconv.ParseFloat(s, 64)
-
- if err != nil {
- return fmt.Errorf("invalid float: %v value=%v", name, s)
- }
-
- fv.SetFloat(i)
- } else {
- // not support or just ignore it?
- // return fmt.Errorf("type not support: field=%v(%v) value=%v(%v)", name, ft.Kind(), val, vt.Kind())
- }
- return nil
- }
-
- func parseTag(tag string) (string, string) {
- tags := strings.Split(tag, ",")
-
- if len(tags) <= 0 {
- return "", ""
- }
-
- if len(tags) == 1 {
- return tags[0], ""
- }
-
- return tags[0], tags[1]
- }
|