Skip to content

Commit 7ac83be

Browse files
committed
DATAREST-1232 - Fix AssociationUriResolving with snake case properties
1 parent 164bc5c commit 7ac83be

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

spring-data-rest-tests/spring-data-rest-tests-jpa/src/main/java/org/springframework/data/rest/webmvc/jpa/Person.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public class Person {
5858
@ManyToOne //
5959
private Person father;
6060

61+
@ManyToOne //
62+
private Person grandFather;
63+
6164
@Description("Timestamp this person object was created") //
6265
private Date created;
6366

@@ -125,6 +128,14 @@ public void setFather(Person father) {
125128
this.father = father;
126129
}
127130

131+
public Person getGrandFather() {
132+
return grandFather;
133+
}
134+
135+
public void setGrandFather(Person grandFather) {
136+
this.grandFather = grandFather;
137+
}
138+
128139
public Date getCreated() {
129140
return created;
130141
}

spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.junit.Test;
2929
import org.junit.runner.RunWith;
3030
import org.springframework.beans.factory.annotation.Autowired;
31+
import org.springframework.beans.factory.annotation.Qualifier;
3132
import org.springframework.context.annotation.Bean;
3233
import org.springframework.context.annotation.Configuration;
3334
import org.springframework.context.support.MessageSourceAccessor;
@@ -68,6 +69,7 @@
6869
import org.springframework.web.util.UriTemplate;
6970

7071
import com.fasterxml.jackson.databind.ObjectMapper;
72+
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
7173
import com.jayway.jsonpath.JsonPath;
7274

7375
/**
@@ -85,7 +87,8 @@ public class PersistentEntitySerializationTests {
8587

8688
private static final String PERSON_JSON_IN = "{\"firstName\": \"John\",\"lastName\": \"Doe\"}";
8789

88-
@Autowired ObjectMapper mapper;
90+
@Autowired @Qualifier("objectMapper") ObjectMapper mapper;
91+
@Autowired @Qualifier("snakeCaseObjectMapper") ObjectMapper snakeCaseMapper;
8992
@Autowired Repositories repositories;
9093
@Autowired PersonRepository people;
9194
@Autowired OrderRepository orders;
@@ -94,7 +97,7 @@ public class PersistentEntitySerializationTests {
9497
@Configuration
9598
static class TestConfig extends RepositoryTestsConfig {
9699

97-
@Bean
100+
@Bean("objectMapper")
98101
@Override
99102
public ObjectMapper objectMapper() {
100103

@@ -103,6 +106,16 @@ public ObjectMapper objectMapper() {
103106
new JacksonSerializers(new EnumTranslator(new MessageSourceAccessor(new StaticMessageSource()))));
104107
return objectMapper;
105108
}
109+
110+
@Bean("snakeCaseObjectMapper")
111+
public ObjectMapper snakeCaseObjectMapper() {
112+
113+
ObjectMapper objectMapper = super.objectMapper();
114+
objectMapper.registerModule(
115+
new JacksonSerializers(new EnumTranslator(new MessageSourceAccessor(new StaticMessageSource()))));
116+
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
117+
return objectMapper;
118+
}
106119
}
107120

108121
LinkDiscoverer linkDiscoverer;
@@ -196,6 +209,17 @@ public void deserializesEmbeddedAssociationsCorrectly() throws Exception {
196209
assertThat(order.getLineItems()).hasSize(2);
197210
}
198211

212+
@Test // DATAREST-1232
213+
public void deserializesPersonWithSnakeCaseLinkToOtherPersonCorrectly() throws Exception {
214+
215+
Person grandFather = people.save(new Person("John", "Doe"));
216+
217+
String child = String.format("{ \"first_name\" : \"Bilbo\", \"grand_father\" : \"/persons/%s\"}", grandFather.getId());
218+
Person result = snakeCaseMapper.readValue(child, Person.class);
219+
220+
assertThat(result.getGrandFather()).isEqualTo(grandFather);
221+
}
222+
199223
@Test // DATAREST-250
200224
public void serializesReferencesWithinPagedResourceCorrectly() throws Exception {
201225

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import com.fasterxml.jackson.databind.JsonDeserializer;
7272
import com.fasterxml.jackson.databind.JsonMappingException;
7373
import com.fasterxml.jackson.databind.JsonSerializer;
74+
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
7475
import com.fasterxml.jackson.databind.SerializationConfig;
7576
import com.fasterxml.jackson.databind.SerializerProvider;
7677
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
@@ -414,6 +415,8 @@ public static class AssociationUriResolvingDeserializerModifier extends BeanDese
414415
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc,
415416
BeanDeserializerBuilder builder) {
416417

418+
boolean isSnakeCase = isSnakeCaseObjectMapper(config);
419+
417420
Iterator<SettableBeanProperty> properties = builder.getProperties();
418421

419422
entities.getPersistentEntity(beanDesc.getBeanClass()).ifPresent(entity -> {
@@ -422,7 +425,7 @@ public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanD
422425

423426
SettableBeanProperty property = properties.next();
424427

425-
PersistentProperty<?> persistentProperty = entity.getPersistentProperty(property.getName());
428+
PersistentProperty<?> persistentProperty = entity.getPersistentProperty(isSnakeCase ? toCamelCase(property.getName()) : property.getName());
426429

427430
if (persistentProperty == null) {
428431
continue;
@@ -465,6 +468,41 @@ private static JsonDeserializer<?> wrapIfCollection(PersistentProperty<?> proper
465468
CollectionValueInstantiator instantiator = new CollectionValueInstantiator(property);
466469
return new CollectionDeserializer(collectionType, elementDeserializer, null, instantiator);
467470
}
471+
472+
private static boolean isSnakeCaseObjectMapper(DeserializationConfig config) {
473+
474+
if (config.getPropertyNamingStrategy() == null) {
475+
return false;
476+
}
477+
478+
return config.getPropertyNamingStrategy().getClass().equals(PropertyNamingStrategy.SnakeCaseStrategy.class);
479+
}
480+
481+
private static String toCamelCase(String value) {
482+
483+
if (!StringUtils.hasText(value)) {
484+
return value;
485+
}
486+
487+
if (!value.contains("_")) {
488+
return value;
489+
}
490+
491+
StringBuilder b = new StringBuilder();
492+
int length = value.length();
493+
494+
for (int i = 0; i < length; i++) {
495+
char ch = value.charAt(i);
496+
if ((ch == '_') && (i < length - 1)) {
497+
b.append(Character.toUpperCase(value.charAt(i + 1)));
498+
i++;
499+
} else if (ch != '_') {
500+
b.append(ch);
501+
}
502+
}
503+
504+
return b.toString();
505+
}
468506
}
469507

470508
/**

0 commit comments

Comments
 (0)