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 c2a9e6f0d..f618bfc27 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 @@ -216,7 +216,7 @@ public class DomainObjectReader { } PersistentEntity entity = candidate.get(); - 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 740da5d20..863f21d6e 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 { this.objectMapper = objectMapper; this.currentType = persistentEntity; this.currentProperties = persistentEntity// - .map(it -> MappedProperties.fromJacksonProperties(it, objectMapper))// + .map(it -> MappedProperties.forSerialization(it, objectMapper))// .orElseGet(() -> MappedProperties.none()); this.currentWrappedProperties = persistentEntity// .map(it -> WrappedProperties.fromJacksonProperties(persistentEntities, it, 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 596bfe05e..10f293753 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 @@ -32,7 +32,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; @@ -89,16 +91,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 6569a73aa..d3cfae07f 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 @@ -36,6 +36,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; @@ -202,8 +203,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 Optional 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 3fdce5433..87059198d 100755 --- 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 @@ -37,7 +37,7 @@ public class MappedPropertiesUnitTests { ObjectMapper mapper = new ObjectMapper(); KeyValueMappingContext context = new KeyValueMappingContext<>(); PersistentEntity entity = context.getRequiredPersistentEntity(Sample.class); - MappedProperties properties = MappedProperties.fromJacksonProperties(entity, mapper); + MappedProperties properties = MappedProperties.forDeserialization(entity, mapper); @Test // DATAREST-575 public void doesNotExposeMappedPropertyForNonSpringDataPersistentProperty() { @@ -75,6 +75,15 @@ public class MappedPropertiesUnitTests { assertThat(properties.getPersistentProperty("readOnlyProperty")).isNull(); } + @Test // DATAREST-1248 + public void doesNotExcludeReadOnlyPropertiesForSerialization() { + + MappedProperties properties = MappedProperties.forSerialization(entity, mapper); + + assertThat(properties.hasPersistentPropertyForField("readOnlyProperty")).isTrue(); + assertThat(properties.getPersistentProperty("readOnlyProperty")).isNotNull(); + } + 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 6cd795477..411b857ae 100755 --- 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 @@ -34,6 +34,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; @@ -171,12 +172,22 @@ public class SortTranslatorUnitTests { assertThat(translatedSort.getOrderFor("burrito.embedded.name")).isNotNull(); } + @Test // DATAREST-1248 + public void allowsSortingByReadOnlyProperty() { + + Sort sort = sortTranslator.translateSort(Sort.by("readOnly"), + mappingContext.getRequiredPersistentEntity(Plain.class)); + + assertThat(sort.getOrderFor("readOnly")).isNotNull(); + } + 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 {