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
pull/336/head
Andy Wilkinson 11 years ago
parent 8de9890757
commit ca7201b4b2

@ -18,6 +18,7 @@ package org.springframework.boot.bind;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -232,11 +233,19 @@ public class RelaxedDataBinder extends DataBinder {
&& !descriptor.getType().equals(Object.class)) { && !descriptor.getType().equals(Object.class)) {
return; 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<String, Object>(); Object extend = new LinkedHashMap<String, Object>();
if (descriptor.isCollection()) { if (descriptor.isCollection()) {
extend = new ArrayList<Object>(); extend = new ArrayList<Object>();
} }
wrapper.setPropertyValue(path.prefix(index + 1), extend); wrapper.setPropertyValue(extensionName, extend);
} }
private String getActualPropertyName(BeanWrapper target, String prefix, String name) { private String getActualPropertyName(BeanWrapper target, String prefix, String name) {

@ -380,6 +380,22 @@ public class RelaxedDataBinderTests {
assertEquals("123", map.get("value")); assertEquals("123", map.get("value"));
} }
@SuppressWarnings("unchecked")
@Test
public void testBindOverlappingNestedMaps() throws Exception {
Map<String, Object> target = new LinkedHashMap<String, Object>();
BindingResult result = bind(target, "a.b.c.d: abc\na.b.c1.d1: efg");
assertEquals(0, result.getErrorCount());
Map<String, Object> a = (Map<String, Object>) target.get("a");
Map<String, Object> b = (Map<String, Object>) a.get("b");
Map<String, Object> c = (Map<String, Object>) b.get("c");
assertEquals("abc", c.get("d"));
Map<String, Object> c1 = (Map<String, Object>) b.get("c1");
assertEquals("efg", c1.get("d1"));
}
private BindingResult bind(Object target, String values) throws Exception { private BindingResult bind(Object target, String values) throws Exception {
return bind(target, values, null); return bind(target, values, null);
} }

Loading…
Cancel
Save