Skip to content

Commit

Permalink
Handle empty indexes for slices and arrays (eg. field[]=1&field[]=2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Maciej Mucha committed May 23, 2024
1 parent 8785d3c commit e47d0ab
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 30 deletions.
56 changes: 42 additions & 14 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ func (d *decoder) parseMapData() {
log.Panicf(errMissingStartBracket, k)
}

insideBracket = false

// ignore empty key
if i == idx+1 {
continue
}

if rd = d.findAlias(k[:idx]); rd == nil {

l = len(d.dm) + 1
Expand Down Expand Up @@ -122,8 +129,6 @@ func (d *decoder) parseMapData() {
}

rd.keys = append(rd.keys, ke)

insideBracket = false
default:
// checking if not a number, 0-9 is 48-57 in byte, see for yourself fmt.Println('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
if insideBracket && (k[i] > 57 || k[i] < 48) {
Expand Down Expand Up @@ -356,16 +361,24 @@ func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx in
d.parseMapData()
// slice elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}

if ok && len(arr) > 0 {
var varr reflect.Value
// handle also param[]=1&param[]=2 syntax
arraySuffix := []byte("[]")
suffixNamespace := append(namespace, arraySuffix...)

var ol int
for _, namespace := range [][]byte{namespace, suffixNamespace} {
arr, ok := d.values[string(namespace)]
l := len(arr)

if !ok || l == 0 {
continue
}

var varr reflect.Value
var ol int

if v.IsNil() {
varr = reflect.MakeSlice(v.Type(), len(arr), len(arr))
varr = reflect.MakeSlice(v.Type(), l, l)
} else {

ol = v.Len()
l += ol

Expand Down Expand Up @@ -456,14 +469,25 @@ func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx in

case reflect.Array:
d.parseMapData()

// array elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}

if ok && len(arr) > 0 {
var varr reflect.Value
// handle also param[]=1&param[]=2 syntax
arraySuffix := []byte("[]")
suffixNamespace := append(namespace, arraySuffix...)

var ol int

for _, namespace := range [][]byte{namespace, suffixNamespace} {
arr, ok := d.values[string(namespace)]
l := len(arr)
overCapacity := v.Len() < l
if overCapacity {

if !ok || l == 0 {
continue
}

var varr reflect.Value

if v.Len() < l {
// more values than array capacity, ignore values over capacity as it's possible some would just want
// to grab the first x number of elements; in the future strict mode logic should return an error
fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
Expand All @@ -474,15 +498,19 @@ func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx in
if v.Len() < len(arr) {
l = v.Len()
}
for i := 0; i < l; i++ {
l += ol

for i := ol; i < l; i++ {
newVal := reflect.New(v.Type().Elem()).Elem()

if d.setFieldByType(newVal, namespace, i) {
if d.setFieldByType(newVal, namespace, i-ol) {
set = true
varr.Index(i).Set(newVal)
}
}

v.Set(varr)
ol = l
}

// maybe it's an numbered array i.e. Phone[0].Number
Expand Down
44 changes: 28 additions & 16 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1910,29 +1910,41 @@ func TestDecoder_EmptyArrayBool(t *testing.T) {
Equal(t, err, nil)
}

func TestDecoder_InvalidSliceIndex(t *testing.T) {
func TestDecoder_ArrayIndexes(t *testing.T) {
type PostsRequest struct {
PostIds []string
StrSlice []string
MapOfStrSlice map[string][]string
StrSliceExplicitField []string `form:"StrArrExplicitField[]"`
StrArr [4]string
}
in := url.Values{
"PostIds[]": []string{"1", "2"},
"StrArr": []string{"0"},
"StrArr[]": []string{"1", "2"},
"StrArr[3]": []string{"3"},
"StrSlice": []string{"0"},
"StrSlice[]": []string{"1", "2"},
"StrSlice[3]": []string{"3"},
"MapOfStrSlice[test1][]": []string{"0", "1"},
"StrArrExplicitField[]": []string{"0", "1"},
}

v := new(PostsRequest)
d := NewDecoder()
err := d.Decode(v, in)
NotEqual(t, err, nil)
Equal(t, err.Error(), "Field Namespace:PostIds ERROR:invalid slice index ''")

// No error with proper name
type PostsRequest2 struct {
PostIds []string `form:"PostIds[]"`
}

v2 := new(PostsRequest2)
err = d.Decode(v2, in)
Equal(t, err, nil)
Equal(t, len(v2.PostIds), 2)
Equal(t, v2.PostIds[0], "1")
Equal(t, v2.PostIds[1], "2")
Equal(t, len(v.StrSlice), 4)
Equal(t, v.StrSlice[0], "0")
Equal(t, v.StrSlice[1], "1")
Equal(t, v.StrSlice[2], "2")
Equal(t, v.StrSlice[3], "3")
Equal(t, len(v.StrArr), 4)
Equal(t, v.StrArr[0], "0")
Equal(t, v.StrArr[1], "1")
Equal(t, v.StrArr[2], "2")
Equal(t, v.StrArr[3], "3")
Equal(t, len(v.MapOfStrSlice["test1"]), 2)
Equal(t, v.MapOfStrSlice["test1"][0], "0")
Equal(t, v.MapOfStrSlice["test1"][1], "1")
Equal(t, v.StrSliceExplicitField[0], "0")
Equal(t, v.StrSliceExplicitField[1], "1")
}

0 comments on commit e47d0ab

Please sign in to comment.