diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java index aafc19d6a..91f3104e3 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java @@ -204,11 +204,23 @@ public class DomainObjectReader { */ private static void copyRemainingProperties(MappedProperties properties, Object source, Object target) { - PropertyAccessor sourceAccessor = PropertyAccessorFactory.forDirectFieldAccess(source); - PropertyAccessor targetAccessor = PropertyAccessorFactory.forDirectFieldAccess(target); + PropertyAccessor sourceFieldAccessor = PropertyAccessorFactory.forDirectFieldAccess(source); + PropertyAccessor sourcePropertyAccessor = PropertyAccessorFactory.forBeanPropertyAccess(source); + PropertyAccessor targetFieldAccessor = PropertyAccessorFactory.forDirectFieldAccess(target); + PropertyAccessor targetPropertyAccessor = PropertyAccessorFactory.forBeanPropertyAccess(target); for (String property : properties.getSpringDataUnmappedProperties()) { - targetAccessor.setPropertyValue(property, sourceAccessor.getPropertyValue(property)); + + // If there's a field we can just copy it. + if (targetFieldAccessor.isWritableProperty(property)) { + targetFieldAccessor.setPropertyValue(property, sourceFieldAccessor.getPropertyValue(property)); + continue; + } + + // Otherwise only copy if there's both a getter and setter. + if (targetPropertyAccessor.isWritableProperty(property) && sourcePropertyAccessor.isReadableProperty(property)) { + targetPropertyAccessor.setPropertyValue(property, sourcePropertyAccessor.getPropertyValue(property)); + } } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java index 29bc7135b..329f39851 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java @@ -92,6 +92,7 @@ public class DomainObjectReaderUnitTests { mappingContext.getPersistentEntity(Outer.class); mappingContext.getPersistentEntity(Parent.class); mappingContext.getPersistentEntity(Product.class); + mappingContext.getPersistentEntity(TransientReadOnlyProperty.class); mappingContext.afterPropertiesSet(); PersistentEntities entities = new PersistentEntities(Collections.singleton(mappingContext)); @@ -441,6 +442,15 @@ public class DomainObjectReaderUnitTests { assertThat(result.map.get(Locale.GERMAN), is(new LocalizedValue("schlussendlich"))); } + @Test // DATAREST-987 + public void handlesTransientPropertyWithoutFieldProperly() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + JsonNode node = mapper.readTree("{ \"name\" : \"Foo\" }"); + + reader.readPut((ObjectNode) node, new TransientReadOnlyProperty(), mapper); + } + @SuppressWarnings("unchecked") private static T as(Object source, Class type) { @@ -564,4 +574,15 @@ public class DomainObjectReaderUnitTests { static class LocalizedValue { String value; } + + @JsonAutoDetect(getterVisibility = Visibility.ANY) + static class TransientReadOnlyProperty { + + @Transient + public String getName() { + return null; + } + + public void setName(String name) {} + } }