diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index ee3e805ddea7..e7cf76f77f8c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -649,11 +649,14 @@ else if (value instanceof List list) { value = list.get(index); } else if (value instanceof Map map) { - Class mapKeyType = ph.getResolvableType().getNested(i + 1).asMap().resolveGeneric(0); + ResolvableType mapResolvableType = ph.getResolvableType().getNested(i + 1).asMap(); + Class mapKeyType = mapResolvableType.resolveGeneric(0); + Class mapValueType = mapResolvableType.resolveGeneric(1); // IMPORTANT: Do not pass full property name in here - property editors // must not kick in for map keys but rather only for map values. TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType); Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor); + growMapIfNecessary(map, convertedMapKey, mapValueType, indexedPropertyName.toString()); value = map.get(convertedMapKey); } else if (value instanceof Iterable iterable) { @@ -792,6 +795,17 @@ private void growCollectionIfNecessary(Collection collection, int index, } } + private void growMapIfNecessary(Map map, @Nullable Object convertedMapKey, + @Nullable Class mapValueType, String name) { + + if (mapValueType == null || !isAutoGrowNestedPaths()) { + return; + } + if (!map.containsKey(convertedMapKey) && map.size() < this.autoGrowCollectionLimit) { + map.put(convertedMapKey, newValue(mapValueType, null, name)); + } + } + /** * Get the last component of the path. Also works if not nested. * @param pa property accessor to work on diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperAutoGrowingTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperAutoGrowingTests.java index f83258ae738a..c85fd97700c9 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperAutoGrowingTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperAutoGrowingTests.java @@ -20,7 +20,6 @@ import java.util.Map; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -200,6 +199,13 @@ void getPropertyValueAutoGrowNestedNestedList() { assertThat(bean.getNestedNestedList().get(0).get(0)).singleElement().isInstanceOf(Bean.class); } + @Test + void getPropertyValueAutoGrowNestedNestedMap() { + assertThat(wrapper.getPropertyValue("nestedNestedMap[A][B][C]")).isNotNull(); + assertThat(bean.getNestedNestedMap()).hasSize(1); + assertThat(bean.getNestedNestedMap().get("A").get("B").get("C")).isInstanceOf(Bean.class); + } + @Test void getPropertyValueAutoGrowListNotParameterized() { assertThatExceptionOfType(InvalidPropertyException.class).isThrownBy(() -> @@ -224,7 +230,7 @@ void setPropertyValueAutoGrowNestedMapWithinMap() { assertThat(bean.getNestedMap().get("A").get("B")).isInstanceOf(Bean.class); } - @Test @Disabled // gh-32154 + @Test void setPropertyValueAutoGrowNestedNestedMapWithinMap() { wrapper.setPropertyValue("nestedNestedMap[A][B][C]", new Bean()); assertThat(bean.getNestedNestedMap().get("A").get("B").get("C")).isInstanceOf(Bean.class);