From 2bfa422ee7482fefbfdc4b87e8fd3d4f261e3f33 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 27 Jun 2014 18:42:48 +0200 Subject: [PATCH] DATAREST-328, DATAREST-320 - AssociationOmittingSerializerModifier now correctly considers renamed properties. Previously, if a persistent property was renamed using e.g. @JsonProperty, the lookup of the PersistentProperty failed as BeanPropertyWriter exposes the final (renamed) property name, not the internal one. We now defensively lookup the correct BeanPropertyDescriptor using the external name and use the corresponding internal one for the PersistentProperty lookup. --- .../json/PersistentEntityJackson2Module.java | 28 +++++++- ...rsistentEntityJackson2ModuleUnitTests.java | 69 +++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2ModuleUnitTests.java diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java index 5979e812c..a235b9ca7 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java @@ -61,6 +61,7 @@ import com.fasterxml.jackson.databind.deser.SettableBeanProperty; import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.deser.std.CollectionDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder; @@ -186,7 +187,7 @@ public class PersistentEntityJackson2Module extends SimpleModule { * * @author Oliver Gierke */ - private static class AssociationOmittingSerializerModifier extends BeanSerializerModifier { + static class AssociationOmittingSerializerModifier extends BeanSerializerModifier { private final PersistentEntities entities; private final RepositoryRestConfiguration configuration; @@ -200,7 +201,7 @@ public class PersistentEntityJackson2Module extends SimpleModule { * @param associationLinks must not be {@literal null}. * @param configuration must not be {@literal null}. */ - private AssociationOmittingSerializerModifier(PersistentEntities entities, AssociationLinks associationLinks, + public AssociationOmittingSerializerModifier(PersistentEntities entities, AssociationLinks associationLinks, RepositoryRestConfiguration configuration) { Assert.notNull(entities, "PersistentEntities must not be null!"); @@ -231,7 +232,7 @@ public class PersistentEntityJackson2Module extends SimpleModule { for (BeanPropertyWriter writer : builder.getProperties()) { // Skip exported associations - PersistentProperty persistentProperty = entity.getPersistentProperty(writer.getName()); + PersistentProperty persistentProperty = findProperty(writer.getName(), entity, beanDesc); if (persistentProperty == null) { continue; @@ -253,6 +254,27 @@ public class PersistentEntityJackson2Module extends SimpleModule { return builder; } + + /** + * Returns the {@link PersistentProperty} for the property with the given final name (the name that it will be + * rendered under eventually). + * + * @param finalName the output name the property will be rendered under. + * @param entity the {@link PersistentEntity} to find the property on. + * @param description the Jackson {@link BeanDescription}. + * @return + */ + private PersistentProperty findProperty(String finalName, PersistentEntity entity, + BeanDescription description) { + + for (BeanPropertyDefinition definition : description.findProperties()) { + if (definition.getName().equals(finalName)) { + return entity.getPersistentProperty(definition.getInternalName()); + } + } + + return null; + } } /** diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2ModuleUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2ModuleUnitTests.java new file mode 100644 index 000000000..48d99d229 --- /dev/null +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2ModuleUnitTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.rest.webmvc.json; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.mapping.context.PersistentEntities; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.rest.core.config.RepositoryRestConfiguration; +import org.springframework.data.rest.webmvc.mapping.AssociationLinks; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +/** + * Unit tests for {@link PersistentEntityJackson2Module}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class PersistentEntityJackson2ModuleUnitTests { + + @Mock AssociationLinks associationLinks; + + /** + * @see DATAREST-328, DATAREST-320 + */ + @Test + public void doesNotDropPropertiesWithCustomizedNames() throws Exception { + + MongoMappingContext mappingContext = new MongoMappingContext(); + mappingContext.getPersistentEntity(Sample.class); + PersistentEntities persistentEntities = new PersistentEntities(Arrays.asList(mappingContext)); + + SimpleModule module = new SimpleModule(); + module.setSerializerModifier(new PersistentEntityJackson2Module.AssociationOmittingSerializerModifier( + persistentEntities, associationLinks, new RepositoryRestConfiguration())); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(module); + + assertThat(mapper.writeValueAsString(new Sample()), is(notNullValue())); + } + + static class Sample { + public @JsonProperty("foo") String name; + } +}