Skip to content

Commit afe7739

Browse files
emil2kkisielk
authored andcommitted
Fix slice TextUnmarshaler. (#99)
Fixes handling of cases when a custome type is both a TextUnmarshaler and a slice.
1 parent 4663607 commit afe7739

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

decoder.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ func isEmpty(t reflect.Type, value []string) bool {
143143

144144
// decode fills a struct field using a parsed path.
145145
func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error {
146-
147146
// Get the field walking the struct fields by index.
148147
for _, name := range parts[0].path {
149148
if v.Type().Kind() == reflect.Ptr {
@@ -185,7 +184,8 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
185184

186185
// Get the converter early in case there is one for a slice type.
187186
conv := d.cache.converter(t)
188-
if conv == nil && t.Kind() == reflect.Slice {
187+
m := isTextUnmarshaler(v)
188+
if conv == nil && t.Kind() == reflect.Slice && m.IsSlice {
189189
var items []reflect.Value
190190
elemT := t.Elem()
191191
isPtrElem := elemT.Kind() == reflect.Ptr
@@ -209,7 +209,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
209209
if d.zeroEmpty {
210210
items = append(items, reflect.Zero(elemT))
211211
}
212-
} else if m := isTextUnmarshaler(v); m.IsValid {
212+
} else if m.IsValid {
213213
u := reflect.New(elemT)
214214
if m.IsPtr {
215215
u = reflect.New(reflect.PtrTo(elemT).Elem())
@@ -297,7 +297,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
297297
Index: -1,
298298
}
299299
}
300-
} else if m := isTextUnmarshaler(v); m.IsValid {
300+
} else if m.IsValid {
301301
// If the value implements the encoding.TextUnmarshaler interface
302302
// apply UnmarshalText as the converter
303303
if err := m.Unmarshaler.UnmarshalText([]byte(val)); err != nil {
@@ -346,6 +346,7 @@ func isTextUnmarshaler(v reflect.Value) unmarshaler {
346346
}
347347
if t.Kind() == reflect.Slice {
348348
// if t is a pointer slice, check if it implements encoding.TextUnmarshaler
349+
m.IsSlice = true
349350
if t = t.Elem(); t.Kind() == reflect.Ptr {
350351
t = reflect.PtrTo(t.Elem())
351352
v = reflect.Zero(t)
@@ -364,6 +365,7 @@ func isTextUnmarshaler(v reflect.Value) unmarshaler {
364365
// unmarshaller contains information about a TextUnmarshaler type
365366
type unmarshaler struct {
366367
Unmarshaler encoding.TextUnmarshaler
368+
IsSlice bool
367369
IsPtr bool
368370
IsValid bool
369371
}

decoder_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,3 +1657,37 @@ func TestRegisterConverterOverridesTextUnmarshaler(t *testing.T) {
16571657
t.Errorf("s1.Aa: expected %v, got %v", ts, s1.MyTime)
16581658
}
16591659
}
1660+
1661+
type S20E string
1662+
1663+
func (e *S20E) UnmarshalText(text []byte) error {
1664+
*e = S20E("x")
1665+
return nil
1666+
}
1667+
1668+
type S20 []S20E
1669+
1670+
func (s *S20) UnmarshalText(text []byte) error {
1671+
*s = S20{"a", "b", "c"}
1672+
return nil
1673+
}
1674+
1675+
// Test to ensure that when a custom type based on a slice implements an
1676+
// encoding.TextUnmarshaler interface that it takes precedence over any
1677+
// implementations by its elements.
1678+
func TestTextUnmarshalerTypeSlice(t *testing.T) {
1679+
data := map[string][]string{
1680+
"Value": []string{"a,b,c"},
1681+
}
1682+
s := struct {
1683+
Value S20
1684+
}{}
1685+
decoder := NewDecoder()
1686+
if err := decoder.Decode(&s, data); err != nil {
1687+
t.Error("Error while decoding:", err)
1688+
}
1689+
expected := S20{"a", "b", "c"}
1690+
if !reflect.DeepEqual(expected, s.Value) {
1691+
t.Errorf("Expected %v errors, got %v", expected, s.Value)
1692+
}
1693+
}

0 commit comments

Comments
 (0)