Skip to content

DATAREST-1232 - Fix AssociationUriResolving with snake case properties #291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ public class Person {
@ManyToOne //
private Person father;

@ManyToOne //
private Person grandFather;

@Description("Timestamp this person object was created") //
private Date created;

Expand Down Expand Up @@ -125,6 +128,14 @@ public void setFather(Person father) {
this.father = father;
}

public Person getGrandFather() {
return grandFather;
}

public void setGrandFather(Person grandFather) {
this.grandFather = grandFather;
}

public Date getCreated() {
return created;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
Expand Down Expand Up @@ -68,6 +69,7 @@
import org.springframework.web.util.UriTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.jayway.jsonpath.JsonPath;

/**
Expand All @@ -85,7 +87,8 @@ public class PersistentEntitySerializationTests {

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

@Autowired ObjectMapper mapper;
@Autowired @Qualifier("objectMapper") ObjectMapper mapper;
@Autowired @Qualifier("snakeCaseObjectMapper") ObjectMapper snakeCaseMapper;
@Autowired Repositories repositories;
@Autowired PersonRepository people;
@Autowired OrderRepository orders;
Expand All @@ -94,7 +97,7 @@ public class PersistentEntitySerializationTests {
@Configuration
static class TestConfig extends RepositoryTestsConfig {

@Bean
@Bean("objectMapper")
@Override
public ObjectMapper objectMapper() {

Expand All @@ -103,6 +106,16 @@ public ObjectMapper objectMapper() {
new JacksonSerializers(new EnumTranslator(new MessageSourceAccessor(new StaticMessageSource()))));
return objectMapper;
}

@Bean("snakeCaseObjectMapper")
public ObjectMapper snakeCaseObjectMapper() {

ObjectMapper objectMapper = super.objectMapper();
objectMapper.registerModule(
new JacksonSerializers(new EnumTranslator(new MessageSourceAccessor(new StaticMessageSource()))));
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
return objectMapper;
}
}

LinkDiscoverer linkDiscoverer;
Expand Down Expand Up @@ -196,6 +209,17 @@ public void deserializesEmbeddedAssociationsCorrectly() throws Exception {
assertThat(order.getLineItems()).hasSize(2);
}

@Test // DATAREST-1232
public void deserializesPersonWithSnakeCaseLinkToOtherPersonCorrectly() throws Exception {

Person grandFather = people.save(new Person("John", "Doe"));

String child = String.format("{ \"first_name\" : \"Bilbo\", \"grand_father\" : \"/persons/%s\"}", grandFather.getId());
Person result = snakeCaseMapper.readValue(child, Person.class);

assertThat(result.getGrandFather()).isEqualTo(grandFather);
}

@Test // DATAREST-250
public void serializesReferencesWithinPagedResourceCorrectly() throws Exception {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
Expand Down Expand Up @@ -414,6 +415,8 @@ public static class AssociationUriResolvingDeserializerModifier extends BeanDese
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc,
BeanDeserializerBuilder builder) {

boolean isSnakeCase = isSnakeCaseObjectMapper(config);

Iterator<SettableBeanProperty> properties = builder.getProperties();

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

SettableBeanProperty property = properties.next();

PersistentProperty<?> persistentProperty = entity.getPersistentProperty(property.getName());
PersistentProperty<?> persistentProperty = entity.getPersistentProperty(isSnakeCase ? toCamelCase(property.getName()) : property.getName());

if (persistentProperty == null) {
continue;
Expand Down Expand Up @@ -465,6 +468,41 @@ private static JsonDeserializer<?> wrapIfCollection(PersistentProperty<?> proper
CollectionValueInstantiator instantiator = new CollectionValueInstantiator(property);
return new CollectionDeserializer(collectionType, elementDeserializer, null, instantiator);
}

private static boolean isSnakeCaseObjectMapper(DeserializationConfig config) {

if (config.getPropertyNamingStrategy() == null) {
return false;
}

return config.getPropertyNamingStrategy().getClass().equals(PropertyNamingStrategy.SnakeCaseStrategy.class);
}

private static String toCamelCase(String value) {

if (!StringUtils.hasText(value)) {
return value;
}

if (!value.contains("_")) {
return value;
}

StringBuilder b = new StringBuilder();
int length = value.length();

for (int i = 0; i < length; i++) {
char ch = value.charAt(i);
if ((ch == '_') && (i < length - 1)) {
b.append(Character.toUpperCase(value.charAt(i + 1)));
i++;
} else if (ch != '_') {
b.append(ch);
}
}

return b.toString();
}
}

/**
Expand Down