From 8a9cb2527e12e771d136371b96c197afe98fd0df Mon Sep 17 00:00:00 2001 From: yusing Date: Mon, 16 Dec 2024 06:02:05 +0800 Subject: [PATCH] support deserialize into anonymous fields --- internal/utils/serialization.go | 25 +++++++- internal/utils/serialization_test.go | 87 +++++++++++++++++----------- 2 files changed, 76 insertions(+), 36 deletions(-) diff --git a/internal/utils/serialization.go b/internal/utils/serialization.go index 74b031e..6d17129 100644 --- a/internal/utils/serialization.go +++ b/internal/utils/serialization.go @@ -141,6 +141,28 @@ func Serialize(data any) (SerializedObject, error) { 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 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 mapping := make(map[string]reflect.Value) fieldName := make(map[string]string) - for _, field := range reflect.VisibleFields(dstT) { + fields := extractFields(dstT) + for _, field := range fields { var key string if jsonTag, ok := field.Tag.Lookup("json"); ok { key = strings.Split(jsonTag, ",")[0] diff --git a/internal/utils/serialization_test.go b/internal/utils/serialization_test.go index 29433ee..a868357 100644 --- a/internal/utils/serialization_test.go +++ b/internal/utils/serialization_test.go @@ -9,44 +9,61 @@ import ( . "github.com/yusing/go-proxy/internal/utils/testing" ) -type S = struct { - I int - S string - IS []int - SS []string - MSI map[string]int - MIS map[int]string +func TestSerializeDeserialize(t *testing.T) { + type S struct { + I int + S string + IS []int + SS []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{ - 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"}, -} - -func TestSerialize(t *testing.T) { - s, err := Serialize(testStruct) +func TestDeserializeAnonymousField(t *testing.T) { + type Anon struct { + A, B int + } + var s struct { + Anon + C int + } + err := Deserialize(map[string]any{"a": 1, "b": 2, "c": 3}, &s) ExpectNoError(t, err) - ExpectDeepEqual(t, s, testStructSerialized) -} - -func TestDeserialize(t *testing.T) { - var s S - err := Deserialize(testStructSerialized, &s) - ExpectNoError(t, err) - ExpectDeepEqual(t, s, testStruct) + ExpectEqual(t, s.A, 1) + ExpectEqual(t, s.B, 2) + ExpectEqual(t, s.C, 3) } func TestStringIntConvert(t *testing.T) {