feat: form binding support encoding.TextUnmarshaler

This commit is contained in:
september 2020-04-05 15:04:31 +08:00
parent 3315353c20
commit 2c94ebbaf7
2 changed files with 59 additions and 3 deletions

View File

@ -5,6 +5,7 @@
package binding package binding
import ( import (
"encoding"
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
@ -16,6 +17,11 @@ import (
"github.com/gin-gonic/gin/internal/json" "github.com/gin-gonic/gin/internal/json"
) )
var (
typeTextUnmarshaler = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
typeTime = reflect.TypeOf(time.Time{})
)
var errUnknownType = errors.New("unknown type") var errUnknownType = errors.New("unknown type")
func mapUri(ptr interface{}, m map[string][]string) error { func mapUri(ptr interface{}, m map[string][]string) error {
@ -205,11 +211,15 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
case reflect.String: case reflect.String:
value.SetString(val) value.SetString(val)
case reflect.Struct: case reflect.Struct:
switch value.Interface().(type) { tValue := value.Type()
case time.Time: switch {
case tValue == typeTime:
return setTimeField(val, field, value) return setTimeField(val, field, value)
case reflect.PtrTo(tValue).Implements(typeTextUnmarshaler):
return setTextUnmarshalerField(val, field, value.Addr())
default:
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
} }
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
case reflect.Map: case reflect.Map:
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
default: default:
@ -218,6 +228,11 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
return nil return nil
} }
func setTextUnmarshalerField(val string, field reflect.StructField, value reflect.Value) error {
u := value.Interface().(encoding.TextUnmarshaler)
return u.UnmarshalText(bytesconv.StringToBytes(val))
}
func setIntField(val string, bitSize int, field reflect.Value) error { func setIntField(val string, bitSize int, field reflect.Value) error {
if val == "" { if val == "" {
val = "0" val = "0"

View File

@ -5,6 +5,7 @@
package binding package binding
import ( import (
"github.com/gin-gonic/gin/internal/bytesconv"
"reflect" "reflect"
"testing" "testing"
"time" "time"
@ -279,3 +280,43 @@ func TestMappingIgnoredCircularRef(t *testing.T) {
err := mappingByPtr(&s, formSource{}, "form") err := mappingByPtr(&s, formSource{}, "form")
assert.NoError(t, err) assert.NoError(t, err)
} }
type TestStringWrapper struct {
defined bool
val string
}
func (t *TestStringWrapper) UnmarshalText(text []byte) error {
t.defined = true
t.val = bytesconv.BytesToString(text)
return nil
}
func (t *TestStringWrapper) String() string {
return t.val
}
func (t *TestStringWrapper) Undefined() bool {
return !t.defined
}
func TestMappingTextUnmarshaler(t *testing.T) {
type Query struct {
Name TestStringWrapper `json:"name" form:"name"`
}
q := Query{}
err := mappingByPtr(&q, formSource{}, "form")
assert.NoError(t, err)
assert.True(t, q.Name.Undefined())
assert.Empty(t, q.Name.String())
form := map[string][]string{
"name": {"test"},
}
err = mappingByPtr(&q, formSource(form), "form")
assert.NoError(t, err)
assert.False(t, q.Name.Undefined())
assert.Equal(t, "test", q.Name.String())
}