Skip to content

Commit

Permalink
9 unmarshaling doesnt work for composed types (#10)
Browse files Browse the repository at this point in the history
* add support for embedded fields

* add unmarshal_from_json_map_test for embedding
  • Loading branch information
avivpxi authored Jul 10, 2022
1 parent f6e8426 commit f25b0f5
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 12 deletions.
31 changes: 24 additions & 7 deletions reflection.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@ import (
var unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()

type reflectionInfo struct {
i int
t reflect.Type
path []int
t reflect.Type
}

func (r reflectionInfo) field(target reflect.Value) reflect.Value {
current := target
for _, i := range r.path {
current = current.Field(i)
}
return current
}

func mapStructFields(target interface{}) map[string]reflectionInfo {
Expand All @@ -23,10 +31,21 @@ func mapStructFields(target interface{}) map[string]reflectionInfo {
if result != nil {
return result
}
result = make(map[string]reflectionInfo, t.NumField())
mapTypeFields(t, result, nil)
cacheStore(t, result)
return result
}

func mapTypeFields(t reflect.Type, result map[string]reflectionInfo, path []int) {
num := t.NumField()
result = make(map[string]reflectionInfo, num)
for i := 0; i < num; i++ {
field := t.Field(i)
fieldPath := append(path, i)
if field.Anonymous && field.Type.Kind() == reflect.Struct {
mapTypeFields(field.Type, result, fieldPath)
continue
}
name := field.Tag.Get("json")
if name == "" || name == "-" {
continue
Expand All @@ -35,12 +54,10 @@ func mapStructFields(target interface{}) map[string]reflectionInfo {
name = name[:index]
}
result[name] = reflectionInfo{
i: i,
t: field.Type,
path: fieldPath,
t: field.Type,
}
}
cacheStore(t, result)
return result
}

func reflectStructValue(target interface{}) reflect.Value {
Expand Down
2 changes: 1 addition & 1 deletion unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (d *decoder) populateStruct(structInstance interface{}, result map[string]i
value, isValidType := d.valueByReflectType(refInfo.t, false)
if isValidType {
if value != nil && doPopulate {
field := structValue.Field(refInfo.i)
field := refInfo.field(structValue)
assignValue(field, value)
}
if result != nil {
Expand Down
2 changes: 1 addition & 1 deletion unmarshal_from_json_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (m *mapDecoder) populateStruct(path []string, data map[string]interface{},
value, isValidType := m.valueByReflectType(append(path, key), inputValue, refInfo.t, false)
if isValidType {
if value != nil && doPopulate {
field := structValue.Field(refInfo.i)
field := refInfo.field(structValue)
assignValue(field, value)
}
if result != nil {
Expand Down
20 changes: 18 additions & 2 deletions unmarshal_from_json_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2122,8 +2122,8 @@ func TestUnmarshalFromJSONMapInputVariations(t *testing.T) {
expectedMap[k] = v
}
structValue := reflectStructValue(actualStruct)
for name, info := range mapStructFields(actualStruct) {
field := structValue.Field(info.i)
for name, refInfo := range mapStructFields(actualStruct) {
field := refInfo.field(structValue)
expectedMap[name] = field.Interface()
}
if tt.expectedMapModifier != nil {
Expand Down Expand Up @@ -2242,3 +2242,19 @@ func TestUnmarshalFromJSONMapSpecialInput(t *testing.T) {
})
}
}

func TestUnmarshalFromJSONMapEmbedding(t *testing.T) {
t.Run("test_embedded_values", func(t *testing.T) {
p := parent{}
result, err := UnmarshalFromJSONMap(map[string]interface{}{"field": "value"}, &p)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if p.Field != "value" {
t.Errorf("missing embedded value in struct %+v", p)
}
if len(result) != 1 || result["field"] != "value" {
t.Errorf("missing embedded value in map %+v", result)
}
})
}
26 changes: 25 additions & 1 deletion unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2132,7 +2132,7 @@ func TestUnmarshalInputVariations(t *testing.T) {
}
structValue := reflectStructValue(actualStruct)
for name, refInfo := range mapStructFields(actualStruct) {
field := structValue.Field(refInfo.i)
field := refInfo.field(structValue)
expectedMap[name] = field.Interface()
}
if tt.expectedMapModifier != nil {
Expand Down Expand Up @@ -2266,6 +2266,30 @@ func TestUnmarshalSpecialInput(t *testing.T) {
}
}

type parent struct {
child
}

type child struct {
Field string `json:"field"`
}

func TestEmbedding(t *testing.T) {
t.Run("test_embedded_values", func(t *testing.T) {
p := parent{}
result, err := Unmarshal([]byte(`{"field":"value"}`), &p)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if p.Field != "value" {
t.Errorf("missing embedded value in struct %+v", p)
}
if len(result) != 1 || result["field"] != "value" {
t.Errorf("missing embedded value in map %+v", result)
}
})
}

var extraData = map[string]interface{}{
"extra1": "foo",
"extra2": float64(12),
Expand Down

0 comments on commit f25b0f5

Please sign in to comment.