diff --git a/binding/form_mapping.go b/binding/form_mapping.go index f5f6f3ae93..8a6a97cffd 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -190,6 +190,26 @@ func trySetCustom(val string, value reflect.Value) (isSet bool, err error) { return false, nil } +// trySetArrayOfCustom works as trySetCustom but for an array of custom values +// If the value implements the BindUnmarshaler interface, it will be used to set the values, +// we will return `true` to skip the default value setting. +func trySetArrayOfCustom(vals []string, arrayValue reflect.Value) (isSet bool, err error) { + switch arrayValue.Index(0).Addr().Interface().(type) { + case BindUnmarshaler: + for i, s := range vals { + { + err := arrayValue.Index(i).Addr().Interface().(BindUnmarshaler).UnmarshalParam(s) + if err != nil { + return true, err + } + } + } + return true, nil + default: + return false, nil + } +} + func trySplit(vs []string, field reflect.StructField) (newVs []string, err error) { cfTag := field.Tag.Get("collection_format") if cfTag == "" || cfTag == "multi" { @@ -442,6 +462,11 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val } func setArray(vals []string, value reflect.Value, field reflect.StructField) error { + ok, err := trySetArrayOfCustom(vals, value) + if ok { + return err + } + for i, s := range vals { err := setWithProperType(s, value.Index(i), field) if err != nil { diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index 810315bb1a..ae02f5a411 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -635,3 +635,34 @@ func TestMappingCustomArrayForm(t *testing.T) { expected, _ := convertTo(val) assert.EqualValues(t, expected, s.FileData) } + +func TestMappingSliceWithCustomUnmarshal(t *testing.T) { + var s struct { + HexSlice []customUnmarshalParamHex `form:"hex_slice"` + } + err := mappingByPtr(&s, formSource{"hex_slice": {`f5`, `f6`}}, "form") + assert.NoError(t, err) + + assert.EqualValues(t, []customUnmarshalParamHex{245, 246}, s.HexSlice) +} + +func TestMappingArrayWithCustomUnmarshal(t *testing.T) { + var s struct { + HexArray [2]customUnmarshalParamHex `form:"hex_array"` + } + err := mappingByPtr(&s, formSource{"hex_array": {`f5`, `f6`}}, "form") + assert.NoError(t, err) + + assert.EqualValues(t, [2]customUnmarshalParamHex{245, 246}, s.HexArray) +} + +func TestMappingArrayWithUnknownType(t *testing.T) { + var s struct { + U []customUnmarshalParamHex + } + err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form") + assert.Error(t, err) + + var expectedError *strconv.NumError + assert.ErrorAs(t, err, &expectedError) +}