Skip to content

Commit 011acb4

Browse files
authored
default binder can bind pointer to slice as struct field. For example *[]string (#2608)
1 parent c57fcb3 commit 011acb4

File tree

2 files changed

+149
-71
lines changed

2 files changed

+149
-71
lines changed

bind.go

+20-8
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,14 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri
188188
}
189189
structFieldKind := structField.Kind()
190190
inputFieldName := typeField.Tag.Get(tag)
191-
if typeField.Anonymous && structField.Kind() == reflect.Struct && inputFieldName != "" {
191+
if typeField.Anonymous && structFieldKind == reflect.Struct && inputFieldName != "" {
192192
// if anonymous struct with query/param/form tags, report an error
193193
return errors.New("query/param/form tags are not allowed with anonymous struct field")
194194
}
195195

196196
if inputFieldName == "" {
197197
// If tag is nil, we inspect if the field is a not BindUnmarshaler struct and try to bind data into it (might contains fields with tags).
198-
// structs that implement BindUnmarshaler are binded only when they have explicit tag
198+
// structs that implement BindUnmarshaler are bound only when they have explicit tag
199199
if _, ok := structField.Addr().Interface().(BindUnmarshaler); !ok && structFieldKind == reflect.Struct {
200200
if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil {
201201
return err
@@ -224,34 +224,46 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri
224224
continue
225225
}
226226

227+
// NOTE: algorithm here is not particularly sophisticated. It probably does not work with absurd types like `**[]*int`
228+
// but it is smart enough to handle niche cases like `*int`,`*[]string`,`[]*int` .
229+
230+
// try unmarshalling first, in case we're dealing with an alias to an array type
227231
if ok, err := unmarshalInputsToField(typeField.Type.Kind(), inputValue, structField); ok {
228232
if err != nil {
229233
return err
230234
}
231235
continue
232236
}
233237

234-
// Call this first, in case we're dealing with an alias to an array type
235238
if ok, err := unmarshalInputToField(typeField.Type.Kind(), inputValue[0], structField); ok {
236239
if err != nil {
237240
return err
238241
}
239242
continue
240243
}
241244

242-
numElems := len(inputValue)
243-
if structFieldKind == reflect.Slice && numElems > 0 {
245+
// we could be dealing with pointer to slice `*[]string` so dereference it. There are wierd OpenAPI generators
246+
// that could create struct fields like that.
247+
if structFieldKind == reflect.Pointer {
248+
structFieldKind = structField.Elem().Kind()
249+
structField = structField.Elem()
250+
}
251+
252+
if structFieldKind == reflect.Slice {
244253
sliceOf := structField.Type().Elem().Kind()
254+
numElems := len(inputValue)
245255
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
246256
for j := 0; j < numElems; j++ {
247257
if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
248258
return err
249259
}
250260
}
251-
val.Field(i).Set(slice)
252-
} else if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
253-
return err
261+
structField.Set(slice)
262+
continue
263+
}
254264

265+
if err := setWithProperType(structFieldKind, inputValue[0], structField); err != nil {
266+
return err
255267
}
256268
}
257269
return nil

0 commit comments

Comments
 (0)