From ca7201b4b2811a901c54b7bf1b7f8e313598572f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 11 Feb 2014 13:38:01 +0000 Subject: [PATCH] Fix binding of overlapping nested maps When binding a nested map structure, RelaxedDataBinder pre-populates the target object with default empty maps. Previously, when these structures overlapped, each step in pre-population process could potentially overwrite what had come before it. This led to the output of the pre-population process being incomplete which would lead to a binding failure. This commit updates the pre-population process so that it checks to see if a property's value has already been set by an earlier step in the process. If it has been set, the existing value is now reused rather than being overwritten by a new empty map. Fixes #328 --- .../boot/bind/RelaxedDataBinder.java | 11 ++++++++++- .../boot/bind/RelaxedDataBinderTests.java | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java index 37c003c391..b9c7bb4d6c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java @@ -18,6 +18,7 @@ package org.springframework.boot.bind; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -232,11 +233,19 @@ public class RelaxedDataBinder extends DataBinder { && !descriptor.getType().equals(Object.class)) { return; } + String extensionName = path.prefix(index + 1); + if (wrapper.isReadableProperty(extensionName)) { + Object currentValue = wrapper.getPropertyValue(extensionName); + if ((descriptor.isCollection() && currentValue instanceof Collection) + || (!descriptor.isCollection() && currentValue instanceof Map)) { + return; + } + } Object extend = new LinkedHashMap(); if (descriptor.isCollection()) { extend = new ArrayList(); } - wrapper.setPropertyValue(path.prefix(index + 1), extend); + wrapper.setPropertyValue(extensionName, extend); } private String getActualPropertyName(BeanWrapper target, String prefix, String name) { diff --git a/spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java b/spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java index 53fa188011..138bcef223 100644 --- a/spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java @@ -380,6 +380,22 @@ public class RelaxedDataBinderTests { assertEquals("123", map.get("value")); } + @SuppressWarnings("unchecked") + @Test + public void testBindOverlappingNestedMaps() throws Exception { + Map target = new LinkedHashMap(); + BindingResult result = bind(target, "a.b.c.d: abc\na.b.c1.d1: efg"); + assertEquals(0, result.getErrorCount()); + + Map a = (Map) target.get("a"); + Map b = (Map) a.get("b"); + Map c = (Map) b.get("c"); + assertEquals("abc", c.get("d")); + + Map c1 = (Map) b.get("c1"); + assertEquals("efg", c1.get("d1")); + } + private BindingResult bind(Object target, String values) throws Exception { return bind(target, values, null); }