mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-13 01:02:28 +08:00
feat: add MapToStruct
This commit is contained in:
@@ -294,48 +294,6 @@ func DeepClone(src interface{}) interface{} {
|
|||||||
return result.Interface()
|
return result.Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
// // CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
|
|
||||||
// func CopyProperties(dst, src interface{}) (err error) {
|
|
||||||
// defer func() {
|
|
||||||
// if e := recover(); e != nil {
|
|
||||||
// err = errors.New(fmt.Sprintf("%v", e))
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
|
|
||||||
// dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
|
|
||||||
// srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)
|
|
||||||
|
|
||||||
// if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
|
|
||||||
// return errors.New("CopyProperties: param dst should be struct pointer")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if srcType.Kind() == reflect.Ptr {
|
|
||||||
// srcType, srcValue = srcType.Elem(), srcValue.Elem()
|
|
||||||
// }
|
|
||||||
// if srcType.Kind() != reflect.Struct {
|
|
||||||
// return errors.New("CopyProperties: param src should be a struct or struct pointer")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// dstType, dstValue = dstType.Elem(), dstValue.Elem()
|
|
||||||
|
|
||||||
// propertyNums := dstType.NumField()
|
|
||||||
|
|
||||||
// for i := 0; i < propertyNums; i++ {
|
|
||||||
// property := dstType.Field(i)
|
|
||||||
// propertyValue := srcValue.FieldByName(property.Name)
|
|
||||||
|
|
||||||
// if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if dstValue.Field(i).CanSet() {
|
|
||||||
// dstValue.Field(i).Set(propertyValue)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
|
// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
|
||||||
// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.
|
// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.
|
||||||
func CopyProperties(dst, src interface{}) error {
|
func CopyProperties(dst, src interface{}) error {
|
||||||
@@ -404,3 +362,16 @@ func GbkToUtf8(bs []byte) ([]byte, error) {
|
|||||||
b, err := io.ReadAll(r)
|
b, err := io.ReadAll(r)
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
// Package convertor implements some functions to convert data.
|
// Package convertor implements some functions to convert data.
|
||||||
package convertor
|
package convertor
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
type cloner struct {
|
type cloner struct {
|
||||||
ptrs map[reflect.Type]map[uintptr]reflect.Value
|
ptrs map[reflect.Type]map[uintptr]reflect.Value
|
||||||
@@ -214,3 +217,70 @@ func (c *cloner) cloneStruct(v reflect.Value) reflect.Value {
|
|||||||
|
|
||||||
return clonedStruct
|
return clonedStruct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setStructField(structObj interface{}, fieldName string, fieldValue interface{}) 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 val.CanConvert(fieldVal.Type()) {
|
||||||
|
fieldVal.Set(val.Convert(fieldVal.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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 interface{}, 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 ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -363,3 +363,40 @@ func TestGbkToUtf8(t *testing.T) {
|
|||||||
assert.Equal(true, utf8.Valid(utf8Data))
|
assert.Equal(true, utf8.Valid(utf8Data))
|
||||||
assert.Equal("hello", string(utf8Data))
|
assert.Equal("hello", string(utf8Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapToStruct(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestMapToStruct")
|
||||||
|
|
||||||
|
type Address struct {
|
||||||
|
Street string `json:"street"`
|
||||||
|
Number int `json:"number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Addr *Address `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import (
|
|||||||
- [ToJson](#ToJson)
|
- [ToJson](#ToJson)
|
||||||
- [ToString](#ToString)
|
- [ToString](#ToString)
|
||||||
- [StructToMap](#StructToMap)
|
- [StructToMap](#StructToMap)
|
||||||
|
- [MapToStruct](#MapToStruct)
|
||||||
- [EncodeByte](#EncodeByte)
|
- [EncodeByte](#EncodeByte)
|
||||||
- [DecodeByte](#DecodeByte)
|
- [DecodeByte](#DecodeByte)
|
||||||
- [DeepClone](#DeepClone)
|
- [DeepClone](#DeepClone)
|
||||||
@@ -40,6 +41,7 @@ import (
|
|||||||
- [ToInterface](#ToInterface)
|
- [ToInterface](#ToInterface)
|
||||||
- [Utf8ToGbk](#Utf8ToGbk)
|
- [Utf8ToGbk](#Utf8ToGbk)
|
||||||
- [GbkToUtf8](#GbkToUtf8)
|
- [GbkToUtf8](#GbkToUtf8)
|
||||||
|
- [GbkToUtf8](#GbkToUtf8)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
@@ -356,7 +358,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="StructToMap">StructToMap</span>
|
### <span id="StructToMap">StructToMap</span>
|
||||||
|
|
||||||
<p>Convert struct to map, only convert exported field, struct field tag `json` should be set.</p>
|
<p>Converts struct to map, only convert exported field, struct field tag `json` should be set.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -389,6 +391,59 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="MapToStruct">MapToStruct</span>
|
||||||
|
|
||||||
|
<p>Converts map to struct, only convert exported field, struct field tag `json` should be set.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MapToStruct(m map[string]interface{}, structObj interface{}) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type Address struct {
|
||||||
|
Street string `json:"street"`
|
||||||
|
Number int `json:"number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Addr *Address `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("p.Addr.Street: %s", p.Addr.Street) //test
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="EncodeByte">EncodeByte</span>
|
### <span id="EncodeByte">EncodeByte</span>
|
||||||
|
|
||||||
<p>Encode data to byte slice.</p>
|
<p>Encode data to byte slice.</p>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import (
|
|||||||
- [ToJson](#ToJson)
|
- [ToJson](#ToJson)
|
||||||
- [ToString](#ToString)
|
- [ToString](#ToString)
|
||||||
- [StructToMap](#StructToMap)
|
- [StructToMap](#StructToMap)
|
||||||
|
- [MapToStruct](#MapToStruct)
|
||||||
- [EncodeByte](#EncodeByte)
|
- [EncodeByte](#EncodeByte)
|
||||||
- [DecodeByte](#DecodeByte)
|
- [DecodeByte](#DecodeByte)
|
||||||
- [DeepClone](#DeepClone)
|
- [DeepClone](#DeepClone)
|
||||||
@@ -388,6 +389,59 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="MapToStruct">MapToStruct</span>
|
||||||
|
|
||||||
|
<p>将map转成struct,struct中导出字段需要设置json tag标记</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MapToStruct(m map[string]interface{}, structObj interface{}) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type Address struct {
|
||||||
|
Street string `json:"street"`
|
||||||
|
Number int `json:"number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Addr *Address `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("p.Addr.Street: %s", p.Addr.Street) //test
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="EncodeByte">EncodeByte</span>
|
### <span id="EncodeByte">EncodeByte</span>
|
||||||
|
|
||||||
<p>将data编码成字节切片</p>
|
<p>将data编码成字节切片</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user