Skip to content

Commit

Permalink
gvalue MapToStruct
Browse files Browse the repository at this point in the history
  • Loading branch information
snail007 committed Jan 9, 2024
1 parent a0ce778 commit e7f8aca
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 70 deletions.
54 changes: 28 additions & 26 deletions util/value/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -1180,31 +1180,24 @@ func IndexOf(sliceInterface interface{}, value interface{}) int {

var typeOfBytes = reflect.TypeOf([]byte(nil))

// MapToStruct convert map to struct, _value is a struct, or it's pointer, by default struct tag 'mkey' will be used,
// if 'mkey' not exists the filed name will used as map key, for example: User{} or new(User).
// MapToStruct same with MapToStructWithTag, but `json` is used as tag name by default
func MapToStruct(mapData map[string]interface{}, _value interface{}) (newStruct interface{}, err error) {
return MapToStructWithTag(mapData, _value, "json")
}

// MapToStructWithTag convert map to struct, _value is a pointer to strut,
// if key 'tag' not exists in the map, the filed name will used as map key.
// Returned newStruct is a struct, for example: newStruct.(User) to assert it type.
// Details refer to TestMapToStruct.
// Ignore filed, set tag mkey:"-", for example:
// Ignore filed, set tag tagName:"-", for example:
// User{
// Age int `mkey:"-"`
// Age int `tagName:"-"`
// }
func MapToStruct(mapData map[string]interface{}, _value interface{}, tagName ...string) (newStruct interface{}, err error) {
if IsNil(_value) || (reflect.TypeOf(_value).Kind() != reflect.Struct &&
reflect.TypeOf(_value).Kind() != reflect.Ptr) {
return nil, errors.New("v must be struct or pointer")
}
tag := "mkey"
if len(tagName) == 1 {
tag = tagName[0]
}
var rv reflect.Value

if reflect.TypeOf(_value).Kind() == reflect.Ptr {
rv = reflect.ValueOf(_value).Elem()
} else {
rv = reflect.New(reflect.TypeOf(_value)).Elem()
func MapToStructWithTag(mapData map[string]interface{}, _value interface{}, tag string) (newStruct interface{}, err error) {
if IsNil(_value) || reflect.TypeOf(_value).Kind() != reflect.Ptr {
return nil, errors.New("value must be a pointer to strut")
}

rv := reflect.ValueOf(_value).Elem()
structType := rv.Type()
var value interface{}
found := false
Expand Down Expand Up @@ -1297,8 +1290,8 @@ func MapToStruct(mapData map[string]interface{}, _value interface{}, tagName ...
ivIsPtr = true
iv = reflect.New(field.Type).Interface()
}
e := json.Unmarshal(d, &iv)
if e != nil {
json.Unmarshal(d, &iv)
if IsNil(iv) {
err = fmt.Errorf("unspported json to map or struct field fail, field: %s, type: %s", field.Name, fieldKind.String())
break BREAK
}
Expand All @@ -1312,9 +1305,7 @@ func MapToStruct(mapData map[string]interface{}, _value interface{}, tagName ...
err = fmt.Errorf("unspported struct field type, field: %s, type: %s", field.Name, fieldKind.String())
break BREAK
}
if err != nil {
return nil, err
}

rValue := reflect.ValueOf(value)
if !rValue.IsValid() {
e := fmt.Errorf("unspported field: %s, type: %s", field.Name, fieldKind.String())
Expand All @@ -1323,8 +1314,19 @@ func MapToStruct(mapData map[string]interface{}, _value interface{}, tagName ...
}
return nil, e
}

found = true
fieldVal.Set(rValue)
func() {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%s", e)
}
}()
fieldVal.Set(rValue)
}()
if err != nil {
return nil, err
}
}
if !found {
return nil, errors.New("any filed be mapped")
Expand Down
120 changes: 76 additions & 44 deletions util/value/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ func TestContains(t *testing.T) {
assert.Equal(t, -1, IndexOf(a, 0))
}

type YourStruct struct {
type Struct struct {
Uint8Field uint8
Uint16Field uint16
Uint32Field uint32
Expand All @@ -527,33 +527,35 @@ type YourStruct struct {
StringField string
BytesField []byte
RawBytesField []byte
ErrSliceField []string
BoolField bool
Float32Field float32
Float64Field float64
TimeField time.Time
Int64TimeField time.Time
ErrTimeField time.Time
StructField YourInnerStruct
PtrStructField *YourInnerPtrStruct
ErrStructField YourInnerStruct
MapKey int8 `mkey:"key1"`
StructField InnerStruct
PtrStructField *InnerPtrStruct
MapKeyField int8 `json:"key1"`
privateField int
//map field only map[string]interface{} supported
MapField map[string]interface{}
IgnoreField int `mkey:"-"`
ErrMapField map[string]string
IgnoreField int `json:"-"`
ErrField chan bool
}

type YourInnerStruct struct {
type InnerStruct struct {
InnerIntField int
InnerStrField string
}

type YourInnerPtrStruct struct {
type InnerPtrStruct struct {
InnerIntField int
InnerStrField string
}

type YourSecondStruct struct {
type SecondStruct struct {
AnotherIntField int
AnotherStrField string
}
Expand Down Expand Up @@ -582,6 +584,7 @@ func TestMapToStruct(t *testing.T) {
"InnerIntField": 42,
"InnerStrField": "nested",
},
"StrStructField": `{"InnerIntField":42,"InnerStrField":"nested"}`,
"PtrStructField": map[string]interface{}{
"InnerIntField": 42,
"InnerStrField": "nested",
Expand All @@ -592,51 +595,80 @@ func TestMapToStruct(t *testing.T) {
"IgnoreField": 1,
}

for _, v := range []interface{}{YourStruct{}, new(YourStruct)} {
result, err := MapToStruct(mapData, v, "mkey")
assert.NoError(t, err)
assert.NotNil(t, result)
yourStruct := result.(YourStruct)
// 现在,您可以断言各个字段
assert.Equal(t, uint8(8), yourStruct.Uint8Field)
assert.Equal(t, uint16(16), yourStruct.Uint16Field)
assert.Equal(t, uint32(32), yourStruct.Uint32Field)
assert.Equal(t, uint64(64), yourStruct.Uint64Field)
assert.Equal(t, uint(128), yourStruct.UintField)
assert.Equal(t, int8(-8), yourStruct.Int8Field)
assert.Equal(t, int16(-16), yourStruct.Int16Field)
assert.Equal(t, int32(-32), yourStruct.Int32Field)
assert.Equal(t, int64(-64), yourStruct.Int64Field)
assert.Equal(t, int(-128), yourStruct.IntField)
assert.Equal(t, "test", yourStruct.StringField)
assert.Equal(t, []byte("base64"), yourStruct.BytesField)
assert.Equal(t, []byte("base64"), yourStruct.RawBytesField)
assert.Equal(t, true, yourStruct.BoolField)
assert.Equal(t, float32(3.14), yourStruct.Float32Field)
assert.Equal(t, float64(6.28), yourStruct.Float64Field)
assert.Equal(t, int64(123), yourStruct.Int64TimeField.Unix())
assert.Equal(t, 42, yourStruct.StructField.InnerIntField)
assert.Equal(t, "nested", yourStruct.StructField.InnerStrField)
assert.Equal(t, 42, yourStruct.PtrStructField.InnerIntField)
assert.Equal(t, "nested", yourStruct.PtrStructField.InnerStrField)
assert.Equal(t, 0, yourStruct.privateField)
assert.Equal(t, "123", yourStruct.MapField["abc"])
assert.Equal(t, 0, yourStruct.IgnoreField)
}
// 测试第二个参数是不同的结构体的情况
result, err := MapToStruct(mapData, YourSecondStruct{})
b := &Struct{}
b0, _ := MapToStruct(mapData, b)
assert.Equal(t, *b, b0)

result, err := MapToStructWithTag(mapData, new(Struct), "json")
assert.NoError(t, err)
assert.NotNil(t, result)
resultStruct := result.(Struct)
assert.Equal(t, uint8(8), resultStruct.Uint8Field)
assert.Equal(t, uint16(16), resultStruct.Uint16Field)
assert.Equal(t, uint32(32), resultStruct.Uint32Field)
assert.Equal(t, uint64(64), resultStruct.Uint64Field)
assert.Equal(t, uint(128), resultStruct.UintField)
assert.Equal(t, int8(-8), resultStruct.Int8Field)
assert.Equal(t, int16(-16), resultStruct.Int16Field)
assert.Equal(t, int32(-32), resultStruct.Int32Field)
assert.Equal(t, int64(-64), resultStruct.Int64Field)
assert.Equal(t, int(-128), resultStruct.IntField)
assert.Equal(t, "test", resultStruct.StringField)
assert.Equal(t, []byte("base64"), resultStruct.BytesField)
assert.Equal(t, []byte("base64"), resultStruct.RawBytesField)
assert.Equal(t, true, resultStruct.BoolField)
assert.Equal(t, float32(3.14), resultStruct.Float32Field)
assert.Equal(t, float64(6.28), resultStruct.Float64Field)
assert.Equal(t, int64(123), resultStruct.Int64TimeField.Unix())
assert.Equal(t, 42, resultStruct.StructField.InnerIntField)
assert.Equal(t, "nested", resultStruct.StructField.InnerStrField)
assert.Equal(t, 42, resultStruct.PtrStructField.InnerIntField)
assert.Equal(t, "nested", resultStruct.PtrStructField.InnerStrField)
assert.Equal(t, 42, resultStruct.StructField.InnerIntField)
assert.Equal(t, "nested", resultStruct.StructField.InnerStrField)
assert.Equal(t, 0, resultStruct.privateField)
assert.Equal(t, "123", resultStruct.MapField["abc"])
assert.Equal(t, 0, resultStruct.IgnoreField)

result, err = MapToStruct(gmap.M{"ErrSliceField": nil}, new(Struct))
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(mapData, SecondStruct{})
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(mapData, nil)
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(gmap.M{"ErrTimeField": "abc"}, new(YourStruct))
result, err = MapToStruct(gmap.M{"ErrTimeField": "abc"}, new(Struct))
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(gmap.M{"ErrStructField": new(chan bool)}, new(Struct))
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(gmap.M{"ErrMapField": map[string]interface{}{
"ErrMapField": map[string]string{"abc": "123"},
}}, new(Struct))
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(gmap.M{"ErrMapField": &http.Client{}}, new(Struct))
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(gmap.M{"ErrMapField": nil}, new(Struct))
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(gmap.M{"ErrField": ""}, new(Struct))
assert.Error(t, err)
assert.Nil(t, result)

result, err = MapToStruct(gmap.M{"ErrStructField": new(chan bool)}, new(YourStruct))
result, err = MapToStruct(gmap.M{"ErrSliceField": nil}, new(Struct))
assert.Error(t, err)
assert.Nil(t, result)
}

0 comments on commit e7f8aca

Please sign in to comment.