diff --git a/src/main/java/org/springframework/hateoas/RenderSingleLinks.java b/src/main/java/org/springframework/hateoas/RenderSingleLinks.java
new file mode 100644
index 00000000..a369e68f
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/RenderSingleLinks.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 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;
+
+/**
+ * Whether to render a single {@link Link} as either a single entry or a collection.
+ */
+public enum RenderSingleLinks {
+
+ /**
+ * A single {@link Link} is rendered as a JSON object.
+ */
+ AS_SINGLE,
+
+ /**
+ * A single {@link Link} is rendered as a JSON Array.
+ */
+ AS_ARRAY
+}
diff --git a/src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java b/src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java
index d5feb4a0..3a64f190 100644
--- a/src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java
+++ b/src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java
@@ -25,6 +25,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.LinkDiscoverer;
+import org.springframework.hateoas.RenderSingleLinks;
/**
* Activates hypermedia support in the {@link ApplicationContext}. Will register infrastructure beans available for
@@ -58,7 +59,7 @@ public @interface EnableHypermediaSupport {
*
* @author Oliver Gierke
*/
- static enum HypermediaType {
+ enum HypermediaType {
/**
* HAL - Hypermedia Application Language.
diff --git a/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java b/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java
index 50e22134..6fb1ff4d 100644
--- a/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java
+++ b/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java
@@ -46,6 +46,7 @@ import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.LinkDiscoverers;
import org.springframework.hateoas.RelProvider;
+import org.springframework.hateoas.RenderSingleLinks;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.core.AnnotationRelProvider;
@@ -53,6 +54,7 @@ import org.springframework.hateoas.core.DefaultRelProvider;
import org.springframework.hateoas.core.DelegatingRelProvider;
import org.springframework.hateoas.core.EvoInflectorRelProvider;
import org.springframework.hateoas.hal.CurieProvider;
+import org.springframework.hateoas.hal.HalConfiguration;
import org.springframework.hateoas.hal.HalLinkDiscoverer;
import org.springframework.hateoas.hal.Jackson2HalModule;
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
@@ -76,7 +78,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
*
* @author Oliver Gierke
*/
-class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
+class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private static final String DELEGATING_REL_PROVIDER_BEAN_NAME = "_relProvider";
private static final String LINK_DISCOVERER_REGISTRY_BEAN_NAME = "_linkDiscovererRegistry";
@@ -89,8 +91,10 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
private static final boolean EVO_PRESENT = ClassUtils.isPresent("org.atteo.evo.inflector.English", null);
private final ImportBeanDefinitionRegistrar linkBuilderBeanDefinitionRegistrar = new LinkBuilderBeanDefinitionRegistrar();
+
+ private BeanFactory beanFactory;
- /*
+ /*
* (non-Javadoc)
* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@@ -125,6 +129,16 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
BeanDefinitionBuilder builder = rootBeanDefinition(Jackson2ModuleRegisteringBeanPostProcessor.class);
registerSourcedBeanDefinition(builder, metadata, registry);
}
+
+ try {
+ this.beanFactory.getBean(HalConfiguration.class);
+ } catch (BeansException e) {
+
+ // If no HalConfiguration bean, create a default one.
+ BeanDefinitionBuilder defaultHalConfiguration = rootBeanDefinition(HalConfiguration.class);
+ defaultHalConfiguration.addPropertyValue("renderSingleLinks", RenderSingleLinks.AS_SINGLE);
+ registerSourcedBeanDefinition(defaultHalConfiguration, metadata, registry);
+ }
}
if (!types.isEmpty()) {
@@ -143,6 +157,11 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
registerRelProviderPluginRegistryAndDelegate(registry);
}
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+
/**
* Registers bean definitions for a {@link PluginRegistry} to capture {@link RelProvider} instances. Wraps the
* registry into a {@link DelegatingRelProvider} bean definition backed by the registry.
diff --git a/src/main/java/org/springframework/hateoas/hal/HalConfiguration.java b/src/main/java/org/springframework/hateoas/hal/HalConfiguration.java
new file mode 100644
index 00000000..837832db
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/hal/HalConfiguration.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 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 lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.Wither;
+
+import org.springframework.hateoas.HypermediaConfiguration;
+import org.springframework.hateoas.RenderSingleLinks;
+
+/**
+ * @author Greg Turnquist
+ */
+@AllArgsConstructor
+@NoArgsConstructor
+public class HalConfiguration implements HypermediaConfiguration {
+
+ private @Wither @Getter @Setter RenderSingleLinks renderSingleLinks;
+}
diff --git a/src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java b/src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java
index 729933c1..071864e6 100644
--- a/src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java
+++ b/src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java
@@ -32,6 +32,7 @@ import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.RelProvider;
+import org.springframework.hateoas.RenderSingleLinks;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
@@ -123,13 +124,14 @@ public class Jackson2HalModule extends SimpleModule {
private final CurieProvider curieProvider;
private final EmbeddedMapper mapper;
private final MessageSourceAccessor accessor;
+ private final HalConfiguration halConfiguration;
- public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper, MessageSourceAccessor accessor) {
- this(null, curieProvider, mapper, accessor);
+ public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper, MessageSourceAccessor accessor, HalConfiguration halConfiguration) {
+ this(null, curieProvider, mapper, accessor, halConfiguration);
}
public HalLinkListSerializer(BeanProperty property, CurieProvider curieProvider, EmbeddedMapper mapper,
- MessageSourceAccessor accessor) {
+ MessageSourceAccessor accessor, HalConfiguration halConfiguration) {
super(TypeFactory.defaultInstance().constructType(List.class));
@@ -137,6 +139,7 @@ public class Jackson2HalModule extends SimpleModule {
this.curieProvider = curieProvider;
this.mapper = mapper;
this.accessor = accessor;
+ this.halConfiguration = halConfiguration;
}
/*
@@ -198,7 +201,7 @@ public class Jackson2HalModule extends SimpleModule {
JavaType mapType = typeFactory.constructMapType(HashMap.class, keyType, valueType);
MapSerializer serializer = MapSerializer.construct(new String[] {}, mapType, true, null,
- provider.findKeySerializer(keyType, null), new OptionalListJackson2Serializer(property), null);
+ provider.findKeySerializer(keyType, null), new OptionalListJackson2Serializer(property, halConfiguration), null);
serializer.serialize(sortedLinks, jgen, provider);
}
@@ -246,7 +249,7 @@ public class Jackson2HalModule extends SimpleModule {
@Override
public JsonSerializer> createContextual(SerializerProvider provider, BeanProperty property)
throws JsonMappingException {
- return new HalLinkListSerializer(property, curieProvider, mapper, accessor);
+ return new HalLinkListSerializer(property, curieProvider, mapper, accessor, halConfiguration);
}
/*
@@ -267,15 +270,7 @@ public class Jackson2HalModule extends SimpleModule {
return null;
}
- /*
- * (non-Javadoc)
- * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#isEmpty(java.lang.Object)
- */
- public boolean isEmpty(List value) {
- return isEmpty(null, value);
- }
-
- /*
+ /*
* (non-Javadoc)
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, java.lang.Object)
*/
@@ -368,10 +363,6 @@ public class Jackson2HalModule extends SimpleModule {
return null;
}
- public boolean isEmpty(Collection> value) {
- return isEmpty(null, value);
- }
-
public boolean isEmpty(SerializerProvider provider, Collection> value) {
return value.isEmpty();
}
@@ -401,9 +392,14 @@ public class Jackson2HalModule extends SimpleModule {
private final BeanProperty property;
private final Map, JsonSerializer