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 c70788f1b..f58b95166 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 @@ -219,7 +219,7 @@ public class DomainObjectReader { return mapper.readerForUpdating(target).readValue(root); } - MappedProperties mappedProperties = MappedProperties.fromJacksonProperties(entity, mapper); + MappedProperties mappedProperties = MappedProperties.forDeserialization(entity, mapper); for (Iterator> i = root.fields(); i.hasNext();) { @@ -616,7 +616,7 @@ public class DomainObjectReader { Assert.notNull(entity, "PersistentEntity must not be null!"); Assert.notNull(mapper, "ObjectMapper must not be null!"); - this.properties = MappedProperties.fromJacksonProperties(entity, mapper); + this.properties = MappedProperties.forDeserialization(entity, mapper); this.targetAccessor = new ConvertingPropertyAccessor(entity.getPropertyAccessor(target), new DefaultConversionService()); this.sourceAccessor = entity.getPropertyAccessor(source); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JacksonMappingAwareSortTranslator.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JacksonMappingAwareSortTranslator.java index cb8e62a37..e80062be2 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JacksonMappingAwareSortTranslator.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JacksonMappingAwareSortTranslator.java @@ -226,7 +226,7 @@ public class JacksonMappingAwareSortTranslator { if (persistentEntity != null) { - this.currentProperties = MappedProperties.fromJacksonProperties(currentType, objectMapper); + this.currentProperties = MappedProperties.forSerialization(currentType, objectMapper); this.currentWrappedProperties = WrappedProperties.fromJacksonProperties(persistentEntities, currentType, objectMapper); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java index dc6d7094b..5f24a2ca7 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java @@ -28,7 +28,9 @@ import org.springframework.data.mapping.PersistentProperty; import org.springframework.util.Assert; import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.introspect.ClassIntrospector; @@ -81,16 +83,34 @@ class MappedProperties { } /** - * Creates {@link MappedProperties} for the given {@link PersistentEntity}. + * Creates {@link MappedProperties} for the given {@link PersistentEntity} for deserialization purposes. Will not + * include Jackson-read-only properties. * * @param entity must not be {@literal null}. * @param mapper must not be {@literal null}. * @return */ - public static MappedProperties fromJacksonProperties(PersistentEntity entity, ObjectMapper mapper) { + public static MappedProperties forDeserialization(PersistentEntity entity, ObjectMapper mapper) { - BeanDescription description = INTROSPECTOR.forDeserialization(mapper.getDeserializationConfig(), - mapper.constructType(entity.getType()), mapper.getDeserializationConfig()); + DeserializationConfig config = mapper.getDeserializationConfig(); + BeanDescription description = INTROSPECTOR.forDeserialization(config, mapper.constructType(entity.getType()), + config); + + return new MappedProperties(entity, description); + } + + /** + * Creates {@link MappedProperties} for the given {@link PersistentEntity} for serialization purposes. Includes + * Jackson-read-only properties. + * + * @param entity must not be {@literal null}. + * @param mapper must not be {@literal null}. + * @return + */ + public static MappedProperties forSerialization(PersistentEntity entity, ObjectMapper mapper) { + + SerializationConfig config = mapper.getSerializationConfig(); + BeanDescription description = INTROSPECTOR.forSerialization(config, mapper.constructType(entity.getType()), config); return new MappedProperties(entity, description); } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/WrappedProperties.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/WrappedProperties.java index efa80ea8c..df0c10c88 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/WrappedProperties.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/WrappedProperties.java @@ -34,6 +34,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; @@ -200,8 +201,10 @@ class WrappedProperties { } private BeanDescription getBeanDescription(Class type) { - return INTROSPECTOR.forDeserialization(mapper.getDeserializationConfig(), mapper.constructType(type), - mapper.getDeserializationConfig()); + + SerializationConfig config = mapper.getSerializationConfig(); + + return INTROSPECTOR.forSerialization(config, mapper.constructType(type), config); } private static AnnotatedMember findAnnotatedMember(BeanPropertyDefinition property) { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java index d540c5ce0..526e87c2b 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java @@ -38,7 +38,7 @@ public class MappedPropertiesUnitTests { ObjectMapper mapper = new ObjectMapper(); KeyValueMappingContext context = new KeyValueMappingContext(); KeyValuePersistentEntity entity = context.getPersistentEntity(Sample.class); - MappedProperties properties = MappedProperties.fromJacksonProperties(entity, mapper); + MappedProperties properties = MappedProperties.forDeserialization(entity, mapper); @Test // DATAREST-575 public void doesNotExposeMappedPropertyForNonSpringDataPersistentProperty() { @@ -76,6 +76,15 @@ public class MappedPropertiesUnitTests { assertThat(properties.getPersistentProperty("readOnlyProperty"), is(nullValue())); } + @Test // DATAREST-1248 + public void doesNotExcludeReadOnlyPropertiesForSerialization() { + + MappedProperties properties = MappedProperties.forSerialization(entity, mapper); + + assertThat(properties.hasPersistentPropertyForField("readOnlyProperty"), is(true)); + assertThat(properties.getPersistentProperty("readOnlyProperty"), is(notNullValue())); + } + static class Sample { public @Transient String notExposedBySpringData; diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/SortTranslatorUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/SortTranslatorUnitTests.java index 8a09132a0..c338ad8e6 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/SortTranslatorUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/SortTranslatorUnitTests.java @@ -35,6 +35,7 @@ import org.springframework.data.rest.webmvc.json.JacksonMappingAwareSortTranslat import org.springframework.data.rest.webmvc.mapping.Associations; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.databind.ObjectMapper; @@ -172,12 +173,22 @@ public class SortTranslatorUnitTests { assertThat(translatedSort.getOrderFor("burrito.embedded.name"), is(notNullValue())); } + @Test // DATAREST-1248 + public void allowsSortingByReadOnlyProperty() { + + Sort sort = sortTranslator.translateSort(new Sort("readOnly"), mappingContext.getPersistentEntity(Plain.class)); + + assertThat(sort, is(notNullValue())); + assertThat(sort.getOrderFor("readOnly"), is(notNullValue())); + } + static class Plain { public String name; public Embedded embedded; @Reference public Embedded refEmbedded; @Reference public AnotherRootEntity association; + @JsonProperty(access = Access.READ_ONLY) public String readOnly; } static class UnwrapEmbedded {