Skip to content

Commit efdc407

Browse files
authored
Merge pull request #87 from alexejk/nested_mixed_arrays
2 parents c1e7cf1 + 78203c0 commit efdc407

File tree

8 files changed

+186
-6
lines changed

8 files changed

+186
-6
lines changed

.golangci.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ linters-settings:
1010
- $gostd
1111
- github.com/stretchr/testify
1212
gci:
13-
local-prefixes: github.com/golangci/golangci-lint
13+
sections:
14+
- standard # Standard section: captures all standard packages.
15+
- default
16+
- prefix(alexejk.io/go-xmlrpc)
1417
goconst:
1518
min-len: 2
1619
min-occurrences: 2
@@ -29,7 +32,7 @@ linters-settings:
2932
- wrapperFunc
3033
goimports:
3134
local-prefixes: alexejk.io/go-xmlrpc
32-
gomnd:
35+
mnd:
3336
settings:
3437
mnd:
3538
# don't include the "operation" and "assign"
@@ -63,7 +66,7 @@ linters:
6366
- gocritic
6467
- gofmt
6568
- goimports
66-
- gomnd
69+
- mnd
6770
- goprintffuncname
6871
- gosec
6972
- gosimple

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.5.3
2+
3+
Bugfixes:
4+
* Decoding an `<array>` of mixed types that contains another set of nested `<array>` (with equally mixed types) (#86).
5+
Outer slice would need to be defined as `[]any` and it's up to the user to cast the inner values (including nested slices) to the desired/expected type.
6+
7+
18
## 0.5.2
29

310
Bugfixes:

client_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,88 @@ func TestClient_Call(t *testing.T) {
6868
require.Equal(t, 12345, resp.Index)
6969
}
7070

71+
func TestClient_Github_86(t *testing.T) {
72+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
73+
m := &struct {
74+
Name string `xml:"methodName"`
75+
Params []*ResponseParam `xml:"params>param"`
76+
}{}
77+
body, err := io.ReadAll(r.Body)
78+
require.NoError(t, err, "test server: read body")
79+
80+
err = xml.Unmarshal(body, m)
81+
require.NoError(t, err, "test server: unmarshal body")
82+
83+
require.Equal(t, "di", m.Name)
84+
require.Equal(t, 3, len(m.Params))
85+
86+
expected := []string{"abc", "def", "hij"}
87+
for i, p := range m.Params {
88+
require.Equal(t, expected[i], *p.Value.String)
89+
}
90+
91+
respBody := `<?xml version="1.0"?>
92+
<methodResponse>
93+
<params>
94+
<param>
95+
<value><string>OK</string></value>
96+
</param>
97+
<param>
98+
<value><int>200</int></value>
99+
</param>
100+
<param>
101+
<value>
102+
<array>
103+
<data>
104+
<value><i4>200</i4></value>
105+
<value><array><data><value>Some String</value></data></array></value>
106+
<value>Server: Sip Express Media Server (5.1.0 (x86_64/Linux)) calls: 0 active/0 total/0 connect/0 min</value>
107+
</data>
108+
</array>
109+
</value>
110+
</param>
111+
</params>
112+
</methodResponse>`
113+
_, _ = fmt.Fprintln(w, respBody)
114+
}))
115+
defer ts.Close()
116+
117+
c, err := NewClient(ts.URL)
118+
require.NoError(t, err)
119+
require.NotNil(t, c)
120+
121+
type DiRequest struct {
122+
First string
123+
Second string
124+
Third string
125+
}
126+
127+
type DiResponse struct {
128+
Status string
129+
Code int
130+
Data []any
131+
}
132+
133+
req := &DiRequest{
134+
First: "abc",
135+
Second: "def",
136+
Third: "hij",
137+
}
138+
resp := &DiResponse{}
139+
140+
err = c.Call("di", req, resp)
141+
require.NoError(t, err)
142+
require.Equal(t, "OK", resp.Status)
143+
require.Equal(t, 200, resp.Code)
144+
145+
// Data array decoding validation
146+
require.NotEmpty(t, resp.Data)
147+
require.Len(t, resp.Data, 3)
148+
require.Equal(t, 200, resp.Data[0].(int))
149+
require.Equal(t, []any{"Some String"}, resp.Data[1].([]any))
150+
require.Equal(t, "Server: Sip Express Media Server (5.1.0 (x86_64/Linux)) calls: 0 active/0 total/0 connect/0 min", resp.Data[2].(string))
151+
}
152+
71153
func TestClient_Fault(t *testing.T) {
72154
ts := mockupServer(t, "response_fault.xml")
73155
defer ts.Close()

decode.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,18 @@ func (d *StdDecoder) decodeValue(value *ResponseValue, field reflect.Value) erro
119119

120120
// Array decoding
121121
case value.Array != nil:
122-
if field.Kind() != reflect.Slice {
122+
fieldKind := field.Kind()
123+
124+
// When dealing with nested arrays of []any type, initial kind for the nested array will be reflect.Interface
125+
if fieldKind == reflect.Interface {
126+
// Create a new []interface{} and assign it to field
127+
fieldType := reflect.SliceOf(reflect.TypeOf((*interface{})(nil)).Elem())
128+
fieldKind = reflect.Slice
129+
130+
field.Set(reflect.MakeSlice(fieldType, 0, 0))
131+
}
132+
133+
if fieldKind != reflect.Slice {
123134
return fmt.Errorf(errFormatInvalidFieldType, reflect.Slice.String(), field.Kind().String())
124135
}
125136

decode_test.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ func TestStdDecoder_DecodeRaw(t *testing.T) {
7777
WoBleBobble bool
7878
WoBleBobble2 int
7979
Field2 int `xmlrpc:"2"`
80+
Array []any
8081
}
8182
}{},
8283
expect: &struct {
@@ -86,6 +87,7 @@ func TestStdDecoder_DecodeRaw(t *testing.T) {
8687
WoBleBobble bool
8788
WoBleBobble2 int
8889
Field2 int `xmlrpc:"2"`
90+
Array []any
8991
}
9092
}{
9193
Struct: struct {
@@ -94,12 +96,14 @@ func TestStdDecoder_DecodeRaw(t *testing.T) {
9496
WoBleBobble bool
9597
WoBleBobble2 int
9698
Field2 int `xmlrpc:"2"`
99+
Array []any
97100
}{
98101
Foo: "bar",
99102
Baz: 2,
100103
WoBleBobble: true,
101104
WoBleBobble2: 34,
102105
Field2: 3,
106+
Array: []any{200, "Some String", []any{"Nested String", 10, true}},
103107
},
104108
},
105109
},
@@ -274,6 +278,7 @@ func TestStdDecoder_DecodeRaw_StructFields(t *testing.T) {
274278
WoBleBobble bool
275279
WoBleBobble2 int
276280
Field2 int `xmlrpc:"2"`
281+
Array []any
277282
}
278283
}{},
279284
expect: &struct {
@@ -283,6 +288,7 @@ func TestStdDecoder_DecodeRaw_StructFields(t *testing.T) {
283288
WoBleBobble bool
284289
WoBleBobble2 int
285290
Field2 int `xmlrpc:"2"`
291+
Array []any
286292
}
287293
}{
288294
Struct: struct {
@@ -291,12 +297,14 @@ func TestStdDecoder_DecodeRaw_StructFields(t *testing.T) {
291297
WoBleBobble bool
292298
WoBleBobble2 int
293299
Field2 int `xmlrpc:"2"`
300+
Array []any
294301
}{
295302
Foo: "bar",
296303
Baz: 2,
297304
WoBleBobble: true,
298305
WoBleBobble2: 34,
299306
Field2: 3,
307+
Array: []any{200, "Some String", []any{"Nested String", 10, true}},
300308
},
301309
},
302310
},
@@ -310,6 +318,7 @@ func TestStdDecoder_DecodeRaw_StructFields(t *testing.T) {
310318
WoBleBobble bool
311319
WoBleBobble2 *int
312320
Field2 *int `xmlrpc:"2"`
321+
Array []any
313322
}
314323
}{},
315324
expect: &struct {
@@ -319,6 +328,7 @@ func TestStdDecoder_DecodeRaw_StructFields(t *testing.T) {
319328
WoBleBobble bool
320329
WoBleBobble2 *int
321330
Field2 *int `xmlrpc:"2"`
331+
Array []any
322332
}
323333
}{
324334
Struct: &struct {
@@ -327,12 +337,14 @@ func TestStdDecoder_DecodeRaw_StructFields(t *testing.T) {
327337
WoBleBobble bool
328338
WoBleBobble2 *int
329339
Field2 *int `xmlrpc:"2"`
340+
Array []any
330341
}{
331342
Foo: sPtr("bar"),
332343
Baz: 2,
333344
WoBleBobble: true,
334345
WoBleBobble2: iPtr(34),
335346
Field2: iPtr(3),
347+
Array: []any{200, "Some String", []any{"Nested String", 10, true}},
336348
},
337349
},
338350
},
@@ -407,6 +419,13 @@ func TestStdDecoder_DecodeRaw_Arrays(t *testing.T) {
407419
Array: []any{0, "4099", "O3D217AC", "<c><b>123</b></c>"},
408420
},
409421
},
422+
// Related to issue #86: https://github.com/alexejk/go-xmlrpc/issues/86
423+
"Basic mixed array - with nested mixed array (Github #86)": {
424+
testFile: "response_array_mixed_with_array.xml",
425+
expect: &TestStruct{
426+
Array: []any{10, "s11", true, []any{"Some String", []any{"Nested String", 10, true}}},
427+
},
428+
},
410429
}
411430

412431
for name, tt := range tests {
@@ -456,6 +475,7 @@ func TestStdDecoder_DecodeRaw_Struct_Map(t *testing.T) {
456475
"woBleBobble": true,
457476
"WoBleBobble2": 34,
458477
"2": 3,
478+
"array": []any{200, "Some String", []any{"Nested String", 10, true}},
459479
},
460480
},
461481
},
@@ -622,7 +642,8 @@ func Test_structMemberToFieldName(t *testing.T) {
622642
}
623643
}
624644

625-
func Test_github(t *testing.T) {
645+
// Issue: https://github.com/alexejk/go-xmlrpc/issues/84
646+
func Test_github_84(t *testing.T) {
626647
dec := &StdDecoder{}
627648
decodeTarget := struct {
628649
Array []any

hack/linter.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ GREEN="\033[32m"
66
YELLOW="\033[33m"
77
NORMAL="\033[39m"
88

9-
LINTER_VERSION=1.56.1
9+
LINTER_VERSION=1.58.1
1010

1111
LINTER_BINDIR=$(go env GOPATH)/bin
1212
LINTER_NAME=golangci-lint
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
Issue #86: https://github.com/alexejk/go-xmlrpc/issues/86
4+
Nested array in the mixed array decoding issue
5+
-->
6+
<methodResponse>
7+
<params>
8+
<param>
9+
<value>
10+
<array>
11+
<data>
12+
<value><int>10</int></value>
13+
<value><string>s11</string></value>
14+
<value><boolean>1</boolean></value>
15+
<value>
16+
<array>
17+
<data>
18+
<value>Some String</value>
19+
<value>
20+
<array>
21+
<data>
22+
<value>Nested String</value>
23+
<value><int>10</int></value>
24+
<value><boolean>1</boolean></value>
25+
</data>
26+
</array>
27+
</value>
28+
</data>
29+
</array>
30+
</value>
31+
</data>
32+
</array>
33+
</value>
34+
</param>
35+
</params>
36+
</methodResponse>

testdata/response_struct.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@
2424
<name>2</name>
2525
<value><int>3</int></value>
2626
</member>
27+
<member>
28+
<name>array</name>
29+
<value>
30+
<array>
31+
<data>
32+
<value><int>200</int></value>
33+
<value><string>Some String</string></value>
34+
<value>
35+
<array>
36+
<data>
37+
<value><string>Nested String</string></value>
38+
<value><int>10</int></value>
39+
<value><boolean>1</boolean></value>
40+
</data>
41+
</array>
42+
</value>
43+
</data>
44+
</array>
45+
</value>
46+
</member>
2747
</struct>
2848
</value>
2949
</param>

0 commit comments

Comments
 (0)