From e47d0ab040ce89455ca1fd843b762521fad243ad Mon Sep 17 00:00:00 2001 From: Maciej Mucha Date: Fri, 24 May 2024 00:53:07 +0200 Subject: [PATCH 1/2] Handle empty indexes for slices and arrays (eg. field[]=1&field[]=2) --- decoder.go | 56 ++++++++++++++++++++++++++++++++++++------------- decoder_test.go | 44 ++++++++++++++++++++++++-------------- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/decoder.go b/decoder.go index e212422..6d90f16 100644 --- a/decoder.go +++ b/decoder.go @@ -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 @@ -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) { @@ -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¶m[]=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 @@ -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¶m[]=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") @@ -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 diff --git a/decoder_test.go b/decoder_test.go index 99897ed..78fc3e7 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -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") } From fb76283d5209ef788388867cd314c1a1585c0c4c Mon Sep 17 00:00:00 2001 From: Maciej Mucha Date: Fri, 24 May 2024 02:37:13 +0200 Subject: [PATCH 2/2] Remove unreachable code --- decoder.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/decoder.go b/decoder.go index 6d90f16..f3ed62e 100644 --- a/decoder.go +++ b/decoder.go @@ -116,11 +116,7 @@ func (d *decoder) parseMapData() { // no need to check for error, it will always pass // as we have done the checking to ensure // the value is a number ahead of time. - var err error - ke.ivalue, err = strconv.Atoi(ke.value) - if err != nil { - ke.ivalue = -1 - } + ke.ivalue, _ = strconv.Atoi(ke.value) if ke.ivalue > rd.sliceLen { rd.sliceLen = ke.ivalue