Skip to content

Commit 9f55296

Browse files
committed
Nested list/map/array with constructor binding
Closes gh-34305
1 parent 4591a67 commit 9f55296

File tree

2 files changed

+59
-10
lines changed

2 files changed

+59
-10
lines changed

spring-context/src/main/java/org/springframework/validation/DataBinder.java

+23-10
Original file line numberDiff line numberDiff line change
@@ -1154,23 +1154,36 @@ private static SortedSet<Integer> getIndexes(String paramPath, ValueResolver val
11541154
@SuppressWarnings("unchecked")
11551155
@Nullable
11561156
private <V> V createIndexedValue(
1157-
String paramPath, Class<?> paramType, ResolvableType elementType,
1157+
String paramPath, Class<?> containerType, ResolvableType elementType,
11581158
String indexedPath, ValueResolver valueResolver) {
11591159

11601160
Object value = null;
11611161
Class<?> elementClass = elementType.resolve(Object.class);
1162-
Object rawValue = valueResolver.resolveValue(indexedPath, elementClass);
1163-
if (rawValue != null) {
1164-
try {
1165-
value = convertIfNecessary(rawValue, elementClass);
1166-
}
1167-
catch (TypeMismatchException ex) {
1168-
handleTypeMismatchException(ex, paramPath, paramType, rawValue);
1169-
}
1162+
1163+
if (List.class.isAssignableFrom(elementClass)) {
1164+
value = createList(indexedPath, elementClass, elementType, valueResolver);
1165+
}
1166+
else if (Map.class.isAssignableFrom(elementClass)) {
1167+
value = createMap(indexedPath, elementClass, elementType, valueResolver);
1168+
}
1169+
else if (elementClass.isArray()) {
1170+
value = createArray(indexedPath, elementClass, elementType, valueResolver);
11701171
}
11711172
else {
1172-
value = createObject(elementType, indexedPath + ".", valueResolver);
1173+
Object rawValue = valueResolver.resolveValue(indexedPath, elementClass);
1174+
if (rawValue != null) {
1175+
try {
1176+
value = convertIfNecessary(rawValue, elementClass);
1177+
}
1178+
catch (TypeMismatchException ex) {
1179+
handleTypeMismatchException(ex, paramPath, containerType, rawValue);
1180+
}
1181+
}
1182+
else {
1183+
value = createObject(elementType, indexedPath + ".", valueResolver);
1184+
}
11731185
}
1186+
11741187
return (V) value;
11751188
}
11761189

spring-context/src/test/java/org/springframework/validation/DataBinderConstructTests.java

+36
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,34 @@ void simpleArrayBinding() {
221221
assertThat(target.integerArray()).containsExactly(1, 2);
222222
}
223223

224+
@Test
225+
void nestedListWithinMap() {
226+
MapValueResolver valueResolver = new MapValueResolver(Map.of(
227+
"integerListMap[a][0]", "1", "integerListMap[a][1]", "2",
228+
"integerListMap[b][0]", "3", "integerListMap[b][1]", "4"));
229+
230+
DataBinder binder = initDataBinder(IntegerListMapRecord.class);
231+
binder.construct(valueResolver);
232+
233+
IntegerListMapRecord target = getTarget(binder);
234+
assertThat(target.integerListMap().get("a")).containsExactly(1, 2);
235+
assertThat(target.integerListMap().get("b")).containsExactly(3, 4);
236+
}
237+
238+
@Test
239+
void nestedMapWithinList() {
240+
MapValueResolver valueResolver = new MapValueResolver(Map.of(
241+
"integerMapList[0][a]", "1", "integerMapList[0][b]", "2",
242+
"integerMapList[1][a]", "3", "integerMapList[1][b]", "4"));
243+
244+
DataBinder binder = initDataBinder(IntegerMapListRecord.class);
245+
binder.construct(valueResolver);
246+
247+
IntegerMapListRecord target = getTarget(binder);
248+
assertThat(target.integerMapList().get(0)).containsOnly(Map.entry("a", 1), Map.entry("b", 2));
249+
assertThat(target.integerMapList().get(1)).containsOnly(Map.entry("a", 3), Map.entry("b", 4));
250+
}
251+
224252

225253
@SuppressWarnings("SameParameterValue")
226254
private static DataBinder initDataBinder(Class<?> targetType) {
@@ -317,6 +345,14 @@ private record IntegerArrayRecord(Integer[] integerArray) {
317345
}
318346

319347

348+
private record IntegerMapListRecord(List<Map<String, Integer>> integerMapList) {
349+
}
350+
351+
352+
private record IntegerListMapRecord(Map<String, List<Integer>> integerListMap) {
353+
}
354+
355+
320356
private record MapValueResolver(Map<String, Object> map) implements DataBinder.ValueResolver {
321357

322358
@Override

0 commit comments

Comments
 (0)