Skip to content

Commit cfb0873

Browse files
authored
fix(deepobject): support nested objects in deepObject arrays (#84)
1 parent 92a6789 commit cfb0873

File tree

2 files changed

+62
-10
lines changed

2 files changed

+62
-10
lines changed

deepobject.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -335,26 +335,24 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error {
335335
}
336336

337337
func assignSlice(dst reflect.Value, pathValues fieldOrValue) error {
338-
// Gather up the values
339338
nValues := len(pathValues.fields)
340-
values := make([]string, nValues)
341-
// We expect to have consecutive array indices in the map
339+
340+
// Process each array element by index
342341
for i := 0; i < nValues; i++ {
343342
indexStr := strconv.Itoa(i)
344343
fv, found := pathValues.fields[indexStr]
345344
if !found {
346345
return errors.New("array deepObjects must have consecutive indices")
347346
}
348-
values[i] = fv.value
349-
}
350347

351-
// This could be cleaner, but we can call into assignPathValues to
352-
// avoid recreating this logic.
353-
for i := 0; i < nValues; i++ {
348+
// Get the destination element
354349
dstElem := dst.Index(i).Addr()
355-
err := assignPathValues(dstElem.Interface(), fieldOrValue{value: values[i]})
350+
351+
// assignPathValues handles both simple values (via fv.value) and
352+
// nested objects (via fv.fields) automatically
353+
err := assignPathValues(dstElem.Interface(), fv)
356354
if err != nil {
357-
return fmt.Errorf("error binding array: %w", err)
355+
return fmt.Errorf("error binding array element %d: %w", i, err)
358356
}
359357
}
360358

deepobject_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,57 @@ func TestDeepObject(t *testing.T) {
8484
require.NoError(t, err)
8585
assert.EqualValues(t, srcObj, dstObj)
8686
}
87+
88+
// Item represents an item object for testing array of objects
89+
type Item struct {
90+
Name string `json:"name"`
91+
Value string `json:"value"`
92+
}
93+
94+
func TestDeepObject_ArrayOfObjects(t *testing.T) {
95+
// Test case for:
96+
// name: items
97+
// style: deepObject
98+
// required: false
99+
// explode: true
100+
// schema:
101+
// type: array
102+
// minItems: 1
103+
// items:
104+
// type: object
105+
106+
srcArray := []Item{
107+
{Name: "first", Value: "value1"},
108+
{Name: "second", Value: "value2"},
109+
}
110+
111+
// Marshal the array to deepObject format
112+
marshaled, err := MarshalDeepObject(srcArray, "items")
113+
require.NoError(t, err)
114+
t.Log("Marshaled:", marshaled)
115+
116+
// Expected format for array of objects with explode:true should be:
117+
// items[0][name]=first&items[0][value]=value1&items[1][name]=second&items[1][value]=value2
118+
119+
// Parse the marshaled string into url.Values
120+
params := make(url.Values)
121+
marshaledParts := strings.Split(marshaled, "&")
122+
for _, p := range marshaledParts {
123+
parts := strings.Split(p, "=")
124+
require.Equal(t, 2, len(parts))
125+
params.Set(parts[0], parts[1])
126+
}
127+
128+
// Unmarshal back to the destination array
129+
var dstArray []Item
130+
err = UnmarshalDeepObject(&dstArray, "items", params)
131+
require.NoError(t, err)
132+
133+
// Verify the result matches the source
134+
assert.EqualValues(t, srcArray, dstArray)
135+
assert.Len(t, dstArray, 2)
136+
assert.Equal(t, "first", dstArray[0].Name)
137+
assert.Equal(t, "value1", dstArray[0].Value)
138+
assert.Equal(t, "second", dstArray[1].Name)
139+
assert.Equal(t, "value2", dstArray[1].Value)
140+
}

0 commit comments

Comments
 (0)