support deserialize into anonymous fields

This commit is contained in:
yusing 2024-12-16 06:02:05 +08:00
parent e53d6d216d
commit 8a9cb2527e
2 changed files with 76 additions and 36 deletions

View file

@ -141,6 +141,28 @@ func Serialize(data any) (SerializedObject, error) {
return result, nil return result, nil
} }
func extractFields(t reflect.Type) []reflect.StructField {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return nil
}
var fields []reflect.StructField
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if !field.IsExported() {
continue
}
if field.Anonymous {
fields = append(fields, extractFields(field.Type)...)
} else {
fields = append(fields, field)
}
}
return fields
}
// Deserialize takes a SerializedObject and a target value, and assigns the values in the SerializedObject to the target value. // Deserialize takes a SerializedObject and a target value, and assigns the values in the SerializedObject to the target value.
// Deserialize ignores case differences between the field names in the SerializedObject and the target. // Deserialize ignores case differences between the field names in the SerializedObject and the target.
// //
@ -183,7 +205,8 @@ func Deserialize(src SerializedObject, dst any) E.Error {
needValidate := false needValidate := false
mapping := make(map[string]reflect.Value) mapping := make(map[string]reflect.Value)
fieldName := make(map[string]string) fieldName := make(map[string]string)
for _, field := range reflect.VisibleFields(dstT) { fields := extractFields(dstT)
for _, field := range fields {
var key string var key string
if jsonTag, ok := field.Tag.Lookup("json"); ok { if jsonTag, ok := field.Tag.Lookup("json"); ok {
key = strings.Split(jsonTag, ",")[0] key = strings.Split(jsonTag, ",")[0]

View file

@ -9,44 +9,61 @@ import (
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
type S = struct { func TestSerializeDeserialize(t *testing.T) {
I int type S struct {
S string I int
IS []int S string
SS []string IS []int
MSI map[string]int SS []string
MIS map[int]string MSI map[string]int
MIS map[int]string
}
var testStruct = S{
I: 1,
S: "hello",
IS: []int{1, 2, 3},
SS: []string{"a", "b", "c"},
MSI: map[string]int{"a": 1, "b": 2, "c": 3},
MIS: map[int]string{1: "a", 2: "b", 3: "c"},
}
var testStructSerialized = map[string]any{
"I": 1,
"S": "hello",
"IS": []int{1, 2, 3},
"SS": []string{"a", "b", "c"},
"MSI": map[string]int{"a": 1, "b": 2, "c": 3},
"MIS": map[int]string{1: "a", 2: "b", 3: "c"},
}
t.Run("serialize", func(t *testing.T) {
s, err := Serialize(testStruct)
ExpectNoError(t, err)
ExpectDeepEqual(t, s, testStructSerialized)
})
t.Run("deserialize", func(t *testing.T) {
var s2 S
err := Deserialize(testStructSerialized, &s2)
ExpectNoError(t, err)
ExpectDeepEqual(t, s2, testStruct)
})
} }
var testStruct = S{ func TestDeserializeAnonymousField(t *testing.T) {
I: 1, type Anon struct {
S: "hello", A, B int
IS: []int{1, 2, 3}, }
SS: []string{"a", "b", "c"}, var s struct {
MSI: map[string]int{"a": 1, "b": 2, "c": 3}, Anon
MIS: map[int]string{1: "a", 2: "b", 3: "c"}, C int
} }
err := Deserialize(map[string]any{"a": 1, "b": 2, "c": 3}, &s)
var testStructSerialized = map[string]any{
"I": 1,
"S": "hello",
"IS": []int{1, 2, 3},
"SS": []string{"a", "b", "c"},
"MSI": map[string]int{"a": 1, "b": 2, "c": 3},
"MIS": map[int]string{1: "a", 2: "b", 3: "c"},
}
func TestSerialize(t *testing.T) {
s, err := Serialize(testStruct)
ExpectNoError(t, err) ExpectNoError(t, err)
ExpectDeepEqual(t, s, testStructSerialized) ExpectEqual(t, s.A, 1)
} ExpectEqual(t, s.B, 2)
ExpectEqual(t, s.C, 3)
func TestDeserialize(t *testing.T) {
var s S
err := Deserialize(testStructSerialized, &s)
ExpectNoError(t, err)
ExpectDeepEqual(t, s, testStruct)
} }
func TestStringIntConvert(t *testing.T) { func TestStringIntConvert(t *testing.T) {