#63 - Use collection relation types for embedded collections.
When embedding values into HAL representations we now correctly expose the collection relation type as exposed by RelProvider.getCollectionResourceRelFor(…). Added EmbeddedHalBuilder to ease building these kinds of nested maps easily and use the commonly shared code between Jackson 1 and 2 implementations.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2013 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.hateoas.hal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.core.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Builder class that allows collecting objects under the relation types defined for the objects but moving from the
|
||||
* single resource relation to the collection one, once more than one object of the same type is added.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class HalEmbeddedBuilder {
|
||||
|
||||
private static final String DEFAULT_REL = "content";
|
||||
|
||||
private final Map<String, List<Object>> embeddeds = new HashMap<String, List<Object>>();
|
||||
private final RelProvider provider;
|
||||
|
||||
/**
|
||||
* Creates a new {@link HalEmbeddedBuilder} using the given {@link RelProvider}.
|
||||
*
|
||||
* @param provider can be {@literal null}.
|
||||
*/
|
||||
public HalEmbeddedBuilder(RelProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value to the embeddeds.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public void add(Object value) {
|
||||
|
||||
Class<?> type = ObjectUtils.getResourceType(value);
|
||||
String singleRel = getDefaultedRelFor(type, false);
|
||||
List<Object> currentValue = embeddeds.get(singleRel);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private String getDefaultedRelFor(Class<?> type, boolean forCollection) {
|
||||
|
||||
if (provider == null) {
|
||||
return DEFAULT_REL;
|
||||
}
|
||||
|
||||
String rel = forCollection ? provider.getCollectionResourceRelFor(type) : provider.getSingleResourceRelFor(type);
|
||||
return rel == null ? DEFAULT_REL : rel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the added objects keyed up by their relation types.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, List<Object>> asMap() {
|
||||
return Collections.unmodifiableMap(embeddeds);
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,6 @@ import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.hateoas.core.ObjectUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -170,10 +169,6 @@ public class Jackson1HalModule extends SimpleModule {
|
||||
private final BeanProperty property;
|
||||
private final RelProvider relProvider;
|
||||
|
||||
public HalResourcesSerializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link HalLinkListSerializer}.
|
||||
*/
|
||||
@@ -196,23 +191,10 @@ public class Jackson1HalModule extends SimpleModule {
|
||||
public void serialize(Collection<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
|
||||
JsonGenerationException {
|
||||
|
||||
// sort resources according to their types
|
||||
Map<String, List<Object>> sortedLinks = new HashMap<String, List<Object>>();
|
||||
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(relProvider);
|
||||
|
||||
for (Object resource : value) {
|
||||
|
||||
Class<?> type = ObjectUtils.getResourceType(resource);
|
||||
String relation = relProvider == null ? "content" : relProvider.getSingleResourceRelFor(type);
|
||||
|
||||
if (relation == null) {
|
||||
relation = "content";
|
||||
}
|
||||
|
||||
if (sortedLinks.get(relation) == null) {
|
||||
sortedLinks.put(relation, new ArrayList<Object>());
|
||||
}
|
||||
|
||||
sortedLinks.get(relation).add(resource);
|
||||
builder.add(resource);
|
||||
}
|
||||
|
||||
TypeFactory typeFactory = provider.getConfig().getTypeFactory();
|
||||
@@ -223,7 +205,7 @@ public class Jackson1HalModule extends SimpleModule {
|
||||
MapSerializer serializer = MapSerializer.construct(new String[] {}, mapType, true, null, null,
|
||||
provider.findKeySerializer(keyType, null), new OptionalListSerializer(property));
|
||||
|
||||
serializer.serialize(sortedLinks, jgen, provider);
|
||||
serializer.serialize(builder.asMap(), jgen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.hateoas.core.ObjectUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerationException;
|
||||
@@ -204,18 +203,9 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
*/
|
||||
public static class HalResourcesSerializer extends ContainerSerializer<Collection<?>> implements ContextualSerializer {
|
||||
|
||||
private static final String DEFAULT_REL = "content";
|
||||
|
||||
private final BeanProperty property;
|
||||
private final RelProvider relProvider;
|
||||
|
||||
/**
|
||||
* Creates a new {@link HalLinkListSerializer}.
|
||||
*/
|
||||
public HalResourcesSerializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public HalResourcesSerializer(RelProvider relPorvider) {
|
||||
this(null, relPorvider);
|
||||
}
|
||||
@@ -223,6 +213,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
public HalResourcesSerializer(BeanProperty property, RelProvider relProvider) {
|
||||
|
||||
super(Collection.class, false);
|
||||
|
||||
this.property = property;
|
||||
this.relProvider = relProvider;
|
||||
}
|
||||
@@ -237,22 +228,10 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
public void serialize(Collection<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
|
||||
JsonGenerationException {
|
||||
|
||||
// sort resources according to their types
|
||||
Map<String, List<Object>> sortedLinks = new HashMap<String, List<Object>>();
|
||||
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(relProvider);
|
||||
|
||||
for (Object resource : value) {
|
||||
|
||||
Class<?> type = ObjectUtils.getResourceType(resource);
|
||||
String relation = relProvider == null ? DEFAULT_REL : relProvider.getSingleResourceRelFor(type);
|
||||
|
||||
if (relation == null) {
|
||||
relation = DEFAULT_REL;
|
||||
}
|
||||
|
||||
if (sortedLinks.get(relation) == null) {
|
||||
sortedLinks.put(relation, new ArrayList<Object>());
|
||||
}
|
||||
sortedLinks.get(relation).add(resource);
|
||||
builder.add(resource);
|
||||
}
|
||||
|
||||
TypeFactory typeFactory = provider.getConfig().getTypeFactory();
|
||||
@@ -263,7 +242,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
MapSerializer serializer = MapSerializer.construct(new String[] {}, mapType, true, null,
|
||||
provider.findKeySerializer(keyType, null), new OptionalListJackson2Serializer(property));
|
||||
|
||||
serializer.serialize(sortedLinks, jgen, provider);
|
||||
serializer.serialize(builder.asMap(), jgen, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user