diff --git a/deepobject.go b/deepobject.go index 2742076..c8498fa 100644 --- a/deepobject.go +++ b/deepobject.go @@ -136,11 +136,16 @@ func UnmarshalDeepObject(dst interface{}, paramName string, params url.Values) e if strings.HasPrefix(pName, searchStr) { // trim the parameter name from the full name. pName = pName[len(paramName):] - fieldNames = append(fieldNames, pName) - if len(pValues) != 1 { - return fmt.Errorf("%s has multiple values", pName) + if len(pValues) == 1 { + fieldNames = append(fieldNames, pName) + fieldValues = append(fieldValues, pValues[0]) + } else { + // Non-indexed array format: expand repeated keys into indexed entries + for i, value := range pValues { + fieldNames = append(fieldNames, pName+"["+strconv.Itoa(i)+"]") + fieldValues = append(fieldValues, value) + } } - fieldValues = append(fieldValues, pValues[0]) } } diff --git a/deepobject_test.go b/deepobject_test.go index 2646ad4..12c86f9 100644 --- a/deepobject_test.go +++ b/deepobject_test.go @@ -170,3 +170,38 @@ func TestDeepObject_ArrayOfObjects(t *testing.T) { assert.Equal(t, "second", dstArray[1].Name) assert.Equal(t, "value2", dstArray[1].Value) } + +func TestDeepObject_NonIndexedArray(t *testing.T) { + t.Run("primitive string array", func(t *testing.T) { + params := url.Values{} + params.Add("p[vals]", "a") + params.Add("p[vals]", "b") + + type Obj struct { + Vals []string `json:"vals"` + } + + var dst Obj + err := UnmarshalDeepObject(&dst, "p", params) + require.NoError(t, err) + assert.Equal(t, []string{"a", "b"}, dst.Vals) + }) + + t.Run("object with mixed scalar and non-indexed array", func(t *testing.T) { + params := url.Values{} + params.Set("p[op]", "eq") + params.Add("p[vals]", "a") + params.Add("p[vals]", "b") + + type Filter struct { + Op string `json:"op"` + Vals []string `json:"vals"` + } + + var dst Filter + err := UnmarshalDeepObject(&dst, "p", params) + require.NoError(t, err) + assert.Equal(t, "eq", dst.Op) + assert.Equal(t, []string{"a", "b"}, dst.Vals) + }) +}