Browse Source

Make registered converters take priority over TextUnmarshaler. (#85)

Fixes a bug in #84
pull/90/head
Kamil Kisiel 8 years ago committed by GitHub
parent
commit
8b1100835d
  1. 18
      cache.go
  2. 2
      converter.go
  3. 23
      decoder.go
  4. 19
      decoder_test.go

18
cache.go

@ -18,13 +18,9 @@ var invalidPath = errors.New("schema: invalid path")
func newCache() *cache {
c := cache{
m: make(map[reflect.Type]*structInfo),
conv: make(map[reflect.Kind]Converter),
regconv: make(map[reflect.Type]Converter),
tag: "schema",
}
for k, v := range converters {
c.conv[k] = v
}
return &c
}
@ -32,11 +28,15 @@ func newCache() *cache {
type cache struct {
l sync.RWMutex
m map[reflect.Type]*structInfo
conv map[reflect.Kind]Converter
regconv map[reflect.Type]Converter
tag string
}
// registerConverter registers a converter function for a custom type.
func (c *cache) registerConverter(value interface{}, converterFunc Converter) {
c.regconv[reflect.TypeOf(value)] = converterFunc
}
// parsePath parses a path in dotted notation verifying that it is a valid
// path to a struct field.
//
@ -178,7 +178,7 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
}
}
if isStruct = ft.Kind() == reflect.Struct; !isStruct {
if conv := c.converter(ft); conv == nil {
if c.converter(ft) == nil && builtinConverters[ft.Kind()] == nil {
// Type is not supported.
return
}
@ -196,11 +196,7 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
// converter returns the converter for a type.
func (c *cache) converter(t reflect.Type) Converter {
conv := c.regconv[t]
if conv == nil {
conv = c.conv[t.Kind()]
}
return conv
return c.regconv[t]
}
// ----------------------------------------------------------------------------

2
converter.go

@ -30,7 +30,7 @@ var (
)
// Default converters for basic types.
var converters = map[reflect.Kind]Converter{
var builtinConverters = map[reflect.Kind]Converter{
boolType: convertBool,
float32Type: convertFloat32,
float64Type: convertFloat64,

23
decoder.go

@ -56,7 +56,7 @@ func (d *Decoder) IgnoreUnknownKeys(i bool) {
// RegisterConverter registers a converter function for a custom type.
func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) {
d.cache.regconv[reflect.TypeOf(value)] = converterFunc
d.cache.registerConverter(value, converterFunc)
}
// Decode decodes a map[string][]string to a struct.
@ -196,9 +196,12 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
// Try to get a converter for the element type.
conv := d.cache.converter(elemT)
if conv == nil {
// As we are not dealing with slice of structs here, we don't need to check if the type
// implements TextUnmarshaler interface
return fmt.Errorf("schema: converter not found for %v", elemT)
conv = builtinConverters[elemT.Kind()]
if conv == nil {
// As we are not dealing with slice of structs here, we don't need to check if the type
// implements TextUnmarshaler interface
return fmt.Errorf("schema: converter not found for %v", elemT)
}
}
for key, value := range values {
@ -284,6 +287,16 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
if d.zeroEmpty {
v.Set(reflect.Zero(t))
}
} else if conv != nil {
if value := conv(val); value.IsValid() {
v.Set(value.Convert(t))
} else {
return ConversionError{
Key: path,
Type: t,
Index: -1,
}
}
} else if m := isTextUnmarshaler(v); m.IsValid {
// If the value implements the encoding.TextUnmarshaler interface
// apply UnmarshalText as the converter
@ -295,7 +308,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
Err: err,
}
}
} else if conv != nil {
} else if conv := builtinConverters[t.Kind()]; conv != nil {
if value := conv(val); value.IsValid() {
v.Set(value.Convert(t))
} else {

19
decoder_test.go

@ -1638,3 +1638,22 @@ func TestAnonymousStructField(t *testing.T) {
}
}
}
// Test to ensure that a registered converter overrides the default text unmarshaler.
func TestRegisterConverterOverridesTextUnmarshaler(t *testing.T) {
type MyTime time.Time
s1 := &struct {
MyTime
}{}
decoder := NewDecoder()
ts := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
decoder.RegisterConverter(s1.MyTime, func(s string) reflect.Value { return reflect.ValueOf(ts) })
v1 := map[string][]string{"MyTime": {"4"}, "Bb": {"5"}}
decoder.Decode(s1, v1)
if s1.MyTime != MyTime(ts) {
t.Errorf("s1.Aa: expected %v, got %v", ts, s1.MyTime)
}
}

Loading…
Cancel
Save