Skip to content

Commit

Permalink
Add ZeroEmpty flag on Decoder.
Browse files Browse the repository at this point in the history
Fixes #17, partially fixes #12.
  • Loading branch information
kisielk committed Jan 10, 2014
1 parent c21d52c commit a97e5a1
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 8 deletions.
34 changes: 26 additions & 8 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,27 @@ func NewDecoder() *Decoder {

// Decoder decodes values from a map[string][]string to a struct.
type Decoder struct {
cache *cache
cache *cache
zeroEmpty bool
}

// Change the tag used to locate custome field aliases (default is "schema")
// SetAliasTag changes the tag used to locate custome field aliases.
// The default tag is "schema".
func (d *Decoder) SetAliasTag(tag string) {
d.cache.tag = tag
}

// ZeroEmpty controls the behaviour of empty values in the map.
// If z is true and a key in the map has the empty string as a value
// then the corresponding struct field is set to the zero value.
// If z is false then empty strings are ignored.
//
// The default value is false, that is empty values do not change
// the value of the struct field.
func (d *Decoder) ZeroEmpty(z bool) {
d.zeroEmpty = z
}

// RegisterConverter registers a converter function for a custom type.
func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) {
d.cache.conv[reflect.TypeOf(value)] = converterFunc
Expand Down Expand Up @@ -118,8 +131,9 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart,
}
for key, value := range values {
if value == "" {
// We are just ignoring empty values for now.
continue
if d.zeroEmpty {
items = append(items, reflect.Zero(elemT))
}
} else if item := conv(value); item.IsValid() {
if isPtrElem {
ptr := reflect.New(elemT)
Expand All @@ -136,11 +150,15 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart,
value := reflect.Append(reflect.MakeSlice(t, 0, 0), items...)
v.Set(value)
} else {
if values[0] == "" {
// We are just ignoring empty values for now.
return nil
// Use the last value provided
val := values[len(values)-1]

if val == "" {
if d.zeroEmpty {
v.Set(reflect.Zero(t))
}
} else if conv := d.cache.conv[t]; conv != nil {
if value := conv(values[len(values)-1]); value.IsValid() {
if value := conv(val); value.IsValid() {
v.Set(value)
} else {
return ConversionError{path, -1}
Expand Down
63 changes: 63 additions & 0 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,22 @@ func TestEmptyValue(t *testing.T) {
}
}

func TestEmptyValueZeroEmpty(t *testing.T) {
data := map[string][]string{
"F01": {"", "foo"},
}
s := S5{}
d := NewDecoder()
d.ZeroEmpty(true)
err := d.Decode(&s, data)
if err != nil {
t.Fatal(err)
}
if len(s.F01) != 2 {
t.Errorf("Expected 1 values in F01")
}
}

// ----------------------------------------------------------------------------

type S6 struct {
Expand Down Expand Up @@ -586,3 +602,50 @@ func TestSetAliasTag(t *testing.T) {
t.Fatalf("Bad value: got %q, want %q", s.ID, "foo")
}
}

func TestZeroEmpty(t *testing.T) {
data := map[string][]string{
"F01": {""},
"F03": {"true"},
}
s := S4{1, 1, false}
d := NewDecoder()
d.ZeroEmpty(true)

err := d.Decode(&s, data)
if err != nil {
t.Fatal(err)
}
if s.F01 != 0 {
t.Errorf("F01: got %v, want %v", s.F01, 0)
}
if s.F02 != 1 {
t.Errorf("F02: got %v, want %v", s.F02, 1)
}
if s.F03 != true {
t.Errorf("F03: got %v, want %v", s.F03, true)
}
}

func TestNoZeroEmpty(t *testing.T) {
data := map[string][]string{
"F01": {""},
"F03": {"true"},
}
s := S4{1, 1, false}
d := NewDecoder()
d.ZeroEmpty(false)
err := d.Decode(&s, data)
if err != nil {
t.Fatal(err)
}
if s.F01 != 1 {
t.Errorf("F01: got %v, want %v", s.F01, 1)
}
if s.F02 != 1 {
t.Errorf("F02: got %v, want %v", s.F02, 1)
}
if s.F03 != true {
t.Errorf("F03: got %v, want %v", s.F03, true)
}
}

0 comments on commit a97e5a1

Please sign in to comment.