Skip to content

Bug when serializing resources with HalEmbeddedBuilder #88

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 2 commits 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 @@ -62,20 +62,22 @@ public void add(Object value) {
return;
}

String singleRel = getDefaultedRelFor(type, false);
List<Object> currentValue = embeddeds.get(singleRel);
String multiRel = getDefaultedRelFor(type, true);
List<Object> currentValue = embeddeds.get(multiRel);

if (currentValue == null) {
ArrayList<Object> arrayList = new ArrayList<Object>();
arrayList.add(value);
embeddeds.put(singleRel, arrayList);
} else if (currentValue.size() == 1) {
currentValue.add(value);
embeddeds.remove(singleRel);
embeddeds.put(getDefaultedRelFor(type, true), currentValue);
} else {
currentValue.add(value);
String singleRel = getDefaultedRelFor(type, false);

if(embeddeds.containsKey(singleRel)) {
currentValue = embeddeds.remove(singleRel);
embeddeds.put(multiRel, currentValue);
} else {
currentValue = new ArrayList<Object>();
embeddeds.put(singleRel, currentValue);
}
}

currentValue.add(value);
}

private String getDefaultedRelFor(Class<?> type, boolean forCollection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ public static class OptionalListJackson2Serializer extends ContainerSerializer<O
ContextualSerializer {

private final BeanProperty property;
private JsonSerializer<Object> serializer;

private Map<Class<?>, JsonSerializer<Object>> serializerMap = new HashMap<Class<?>, JsonSerializer<Object>>();
public OptionalListJackson2Serializer() {
this(null);
}
Expand Down Expand Up @@ -351,22 +351,30 @@ private void serializeContents(Iterator<?> value, JsonGenerator jgen, Serializer
if (elem == null) {
provider.defaultSerializeNull(jgen);
} else {
if (serializer == null) {
serializer = provider.findValueSerializer(elem.getClass(), property);
}
serializer.serialize(elem, jgen, provider);
findSerializer(provider, elem.getClass()).serialize(elem, jgen, provider);
}
}
}

private JsonSerializer<Object> findSerializer(SerializerProvider provider, Class<? extends Object> elemType)
throws JsonMappingException {

if(!this.serializerMap.containsKey(elemType)) {
JsonSerializer<Object> serializer = provider.findValueSerializer(elemType, property);
this.serializerMap.put(elemType, serializer);
}
return this.serializerMap.get(elemType);
}

/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that make sense here? It seems we're now arbitrarily picking a serializer to return it, don't we?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that it is just as "random" as it was befor (the type of the first item is not less random)

* (non-Javadoc)
*
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
*/
@Override
public JsonSerializer<?> getContentSerializer() {
return serializer;
Iterator<JsonSerializer<Object>> it = this.serializerMap.values().iterator();
return it.hasNext() ? it.next() : null;
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ public void rendersSingleElementsWithSingleEntityRel() {
@Test
public void rendersMultipleElementsWithCollectionResourceRel() {

Map<String, List<Object>> map = setUpBuilder("foo", "bar", 1L);
Map<String, List<Object>> map = setUpBuilder("lorem", "ipsum", "dolor", 1L);

assertThat(map.containsKey("string"), is(false));
assertThat(map.get("strings"), Matchers.<List<Object>> allOf(hasSize(2), Matchers.<Object> hasItems("foo", "bar")));
assertThat(map.get("strings"), Matchers.<List<Object>> allOf(hasSize(3), Matchers.<Object> hasItems("lorem", "ipsum", "dolor")));
assertThat(map.get("long"), Matchers.<List<Object>> allOf(hasSize(1), hasItem(1L)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg
static final String SINGLE_EMBEDDED_RESOURCE_REFERENCE = "{\"_links\":{\"self\":{\"href\":\"localhost\"}},\"_embedded\":{\"content\":{\"text\":\"test1\",\"number\":1,\"_links\":{\"self\":{\"href\":\"localhost\"}}}}}";
static final String LIST_EMBEDDED_RESOURCE_REFERENCE = "{\"_links\":{\"self\":{\"href\":\"localhost\"}},\"_embedded\":{\"content\":[{\"text\":\"test1\",\"number\":1,\"_links\":{\"self\":{\"href\":\"localhost\"}}},{\"text\":\"test2\",\"number\":2,\"_links\":{\"self\":{\"href\":\"localhost\"}}}]}}";

static final String MIXED_EMBEDDED_RESOURCE_REFERENCE = "{\"_links\":{},\"_embedded\":{\"content\":[{\"roles\":[\"admin\",\"user\"],\"_links\":{\"self\":{\"href\":\"localhost\"}}},{\"text\":\"test2\",\"number\":2,\"_links\":{\"self\":{\"href\":\"localhost\"}}}],\"pojo\":{\"text\":\"test1\",\"number\":1,\"_links\":{\"self\":{\"href\":\"localhost\"}}}}}";

static final String ANNOTATED_EMBEDDED_RESOURCE_REFERENCE = "{\"_links\":{\"self\":{\"href\":\"localhost\"}},\"_embedded\":{\"pojo\":{\"text\":\"test1\",\"number\":1,\"_links\":{\"self\":{\"href\":\"localhost\"}}}}}";
static final String ANNOTATED_EMBEDDED_RESOURCES_REFERENCE = "{\"_links\":{},\"_embedded\":{\"pojos\":[{\"text\":\"test1\",\"number\":1,\"_links\":{\"self\":{\"href\":\"localhost\"}}},{\"text\":\"test2\",\"number\":2,\"_links\":{\"self\":{\"href\":\"localhost\"}}}]}}";

Expand Down Expand Up @@ -174,6 +176,12 @@ public void rendersMultipleResourceResourcesAsEmbedded() throws Exception {
assertThat(write(resources), is(LIST_EMBEDDED_RESOURCE_REFERENCE));
}

@Test
public void rendersMixedResourceResourcesAsEmbedded() throws Exception {
Resources<?> resources = setupMixedResources();
assertThat(write(resources), is(MIXED_EMBEDDED_RESOURCE_REFERENCE));
}

@Test
public void deserializesMultipleResourceResourcesAsEmbedded() throws Exception {

Expand Down Expand Up @@ -286,4 +294,15 @@ private static Resources<Resource<SimplePojo>> setupResources() {

return new Resources<Resource<SimplePojo>>(content);
}

private static Resources<?> setupMixedResources() {

List<ResourceSupport> content = new ArrayList<ResourceSupport>();
String[] roles = { "admin", "user" };
content.add(new SimpleResourcePojo(roles, new Link("localhost")));
content.add(new Resource<SimpleAnnotatedPojo>(new SimpleAnnotatedPojo("test1", 1), new Link("localhost")));
content.add(new Resource<SimplePojo>(new SimplePojo("test2", 2), new Link("localhost")));

return new Resources<ResourceSupport>(content);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.springframework.hateoas.hal;

import static java.util.Arrays.asList;

import java.util.Arrays;

import org.springframework.hateoas.Link;
import org.springframework.hateoas.ResourceSupport;

public class SimpleResourcePojo extends ResourceSupport {

private String[] roles;

public SimpleResourcePojo(String[] roles, Link... links) {
super();
this.roles = roles;

if(links != null) {
add(asList(links));
}
}

public String[] getRoles() {
return roles;
}

public void setRoles(String[] roles) {
this.roles = roles;
}

@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Arrays.hashCode(roles);
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
SimpleResourcePojo other = (SimpleResourcePojo) obj;
if (!Arrays.equals(roles, other.roles))
return false;
return true;
}

}