Skip to content

Commit

Permalink
decoder: Adding support to decode a struct into a map when it's neste…
Browse files Browse the repository at this point in the history
…d into an array
  • Loading branch information
alexejk committed Mar 25, 2024
1 parent 96e03e7 commit efa4f48
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 2 deletions.
15 changes: 13 additions & 2 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,24 @@ func (d *StdDecoder) decodeValue(value *ResponseValue, field reflect.Value) erro
// Struct decoding
case len(value.Struct) != 0:
fieldKind := field.Kind()
fieldType := field.Type()

if fieldKind == reflect.Interface {
// Create a new map[string]any and assign it to field
fieldType = reflect.TypeOf(map[string]any{})
fieldKind = reflect.Map

field.Set(reflect.MakeMap(fieldType))
field = field.Elem() // As the field is an interface, need to unwrap it
}

if fieldKind != reflect.Struct && fieldKind != reflect.Map {
return fmt.Errorf(errFormatInvalidFieldTypeOrType, reflect.Struct.String(), reflect.Map.String(), fieldKind.String())
}

// If we are targeting a map, it should be initialized
if fieldKind == reflect.Map {
if kt := field.Type().Key().Kind(); kt != reflect.String {
if kt := fieldType.Key().Kind(); kt != reflect.String {
return fmt.Errorf(errFormatInvalidMapKeyTypeForStruct, kt.String())
}

Expand All @@ -160,7 +171,7 @@ func (d *StdDecoder) decodeValue(value *ResponseValue, field reflect.Value) erro
for _, m := range value.Struct {
if fieldKind == reflect.Map {
mapKey := reflect.ValueOf(m.Name)
f := reflect.New(field.Type().Elem()).Elem()
f := reflect.New(fieldType.Elem()).Elem()

if err := d.decodeValue(&m.Value, f); err != nil {
return fmt.Errorf("failed decoding struct member '%s': %w", m.Name, err)
Expand Down
14 changes: 14 additions & 0 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,20 @@ func Test_structMemberToFieldName(t *testing.T) {
}
}

func Test_github(t *testing.T) {
dec := &StdDecoder{}
decodeTarget := struct {
Array []any
}{}

err := dec.DecodeRaw(loadTestFile(t, "response_array_mixed_with_struct.xml"), &decodeTarget)
require.NoError(t, err)
require.Equal(t, 3, len(decodeTarget.Array))
require.Equal(t, 200, decodeTarget.Array[0])
require.Equal(t, "OK", decodeTarget.Array[1])
require.Equal(t, "OK", decodeTarget.Array[2].(map[string]any)["status"])
}

func loadTestFile(t *testing.T, name string) []byte {
path := filepath.Join("testdata", name) // relative path

Expand Down
29 changes: 29 additions & 0 deletions testdata/response_array_mixed_with_struct.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value>
<array>
<data>
<value>
<i4>200</i4>
</value>
<value>OK</value>
<value>
<struct>
<member>
<name>status</name>
<value>OK</value>
</member>
<member>
<name>contact</name>
<value>&lt;sip:[email protected]:5060&gt;;expires=60</value>
</member>
</struct>
</value>
</data>
</array>
</value>
</param>
</params>
</methodResponse>

0 comments on commit efa4f48

Please sign in to comment.