mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
feat: add MapToStruct
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
package maputil
|
package maputil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/slice"
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
@@ -303,3 +304,79 @@ func HasKey[K comparable, V any](m map[K]V, key K) bool {
|
|||||||
_, haskey := m[key]
|
_, haskey := m[key]
|
||||||
return haskey
|
return haskey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapToStruct converts map to struct
|
||||||
|
// Play: todo
|
||||||
|
func MapToStruct(m map[string]interface{}, structObj interface{}) error {
|
||||||
|
|
||||||
|
for k, v := range m {
|
||||||
|
err := setStructField(structObj, k, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setStructField(structObj any, fieldName string, fieldValue any) error {
|
||||||
|
structVal := reflect.ValueOf(structObj).Elem()
|
||||||
|
|
||||||
|
fName := getFieldNameByJsonTag(structObj, fieldName)
|
||||||
|
if fName == "" {
|
||||||
|
return fmt.Errorf("Struct field json tag don't match map key : %s in obj", fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldVal := structVal.FieldByName(fName)
|
||||||
|
|
||||||
|
if !fieldVal.IsValid() {
|
||||||
|
return fmt.Errorf("No such field: %s in obj", fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fieldVal.CanSet() {
|
||||||
|
return fmt.Errorf("Cannot set %s field value", fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(fieldValue)
|
||||||
|
|
||||||
|
if fieldVal.Type() != val.Type() {
|
||||||
|
|
||||||
|
if m, ok := fieldValue.(map[string]interface{}); ok {
|
||||||
|
|
||||||
|
if fieldVal.Kind() == reflect.Struct {
|
||||||
|
return MapToStruct(m, fieldVal.Addr().Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
if fieldVal.Kind() == reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
|
||||||
|
if fieldVal.IsNil() {
|
||||||
|
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return MapToStruct(m, fieldVal.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Map value type don't match struct field type")
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldVal.Set(val)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFieldNameByJsonTag(structObj any, jsonTag string) string {
|
||||||
|
s := reflect.TypeOf(structObj).Elem()
|
||||||
|
|
||||||
|
for i := 0; i < s.NumField(); i++ {
|
||||||
|
field := s.Field(i)
|
||||||
|
tag := field.Tag
|
||||||
|
name := tag.Get("json")
|
||||||
|
|
||||||
|
if name == jsonTag {
|
||||||
|
return field.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -472,3 +472,42 @@ func TestHasKey(t *testing.T) {
|
|||||||
assert.Equal(true, HasKey(m, "a"))
|
assert.Equal(true, HasKey(m, "a"))
|
||||||
assert.Equal(false, HasKey(m, "c"))
|
assert.Equal(false, HasKey(m, "c"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapToStruct(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestMapToStruct")
|
||||||
|
|
||||||
|
type (
|
||||||
|
Person struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Addr *Address `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Address struct {
|
||||||
|
Street string `json:"street"`
|
||||||
|
Number int `json:"number"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
m := map[string]interface{}{
|
||||||
|
"name": "Nothin",
|
||||||
|
"age": 28,
|
||||||
|
"phone": "123456789",
|
||||||
|
"address": map[string]interface{}{
|
||||||
|
"street": "test",
|
||||||
|
"number": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var p Person
|
||||||
|
err := MapToStruct(m, &p)
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(m["name"], p.Name)
|
||||||
|
assert.Equal(m["age"], p.Age)
|
||||||
|
assert.Equal(m["phone"], p.Phone)
|
||||||
|
assert.Equal("test", p.Addr.Street)
|
||||||
|
assert.Equal(1, p.Addr.Number)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user