#378 - HAL links get title attributes rendered resolved through a resource bundle.

HalLinkListSerializer now tries to obtain a link title by looking up key _links.$rel.title for both the namespaced (curied) and local link relation if the former doesn't resolve into a message.
This commit is contained in:
Oliver Gierke
2015-08-19 19:07:07 +02:00
parent d77a4b65f5
commit 71ec26fc8d
7 changed files with 191 additions and 22 deletions

View File

@@ -26,6 +26,8 @@ import java.util.List;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.RelProvider;
@@ -34,6 +36,9 @@ import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
@@ -109,20 +114,26 @@ public class Jackson2HalModule extends SimpleModule {
*/
public static class HalLinkListSerializer extends ContainerSerializer<List<Link>>implements ContextualSerializer {
private static final String RELATION_MESSAGE_TEMPLATE = "_links.%s.title";
private final BeanProperty property;
private final CurieProvider curieProvider;
private final EmbeddedMapper mapper;
private final MessageSourceAccessor messageSource;
public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper) {
this(null, curieProvider, mapper);
public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper,
MessageSourceAccessor messageSource) {
this(null, curieProvider, mapper, messageSource);
}
public HalLinkListSerializer(BeanProperty property, CurieProvider curieProvider, EmbeddedMapper mapper) {
public HalLinkListSerializer(BeanProperty property, CurieProvider curieProvider, EmbeddedMapper mapper,
MessageSourceAccessor messageSource) {
super(List.class, false);
this.property = property;
this.curieProvider = curieProvider;
this.mapper = mapper;
this.messageSource = messageSource;
}
/*
@@ -166,7 +177,8 @@ public class Jackson2HalModule extends SimpleModule {
}
links.add(link);
sortedLinks.get(rel).add(link);
sortedLinks.get(rel).add(toHalLink(link));
}
if (!skipCuries && prefixingRequired && curiedLinkPresent) {
@@ -188,6 +200,43 @@ public class Jackson2HalModule extends SimpleModule {
serializer.serialize(sortedLinks, jgen, provider);
}
/**
* Wraps the given link into a HAL specifc extension.
*
* @param link must not be {@literal null}.
* @return
*/
private HalLink toHalLink(Link link) {
String rel = link.getRel();
String title = getTitle(rel);
if (title == null) {
title = getTitle(rel.contains(":") ? rel.substring(rel.indexOf(":") + 1) : rel);
}
return new HalLink(link, title);
}
/**
* Returns the title for the given local link relation resolved through the configured {@link MessageSourceAccessor}
* .
*
* @param localRel must not be {@literal null} or empty.
* @return
*/
private String getTitle(String localRel) {
Assert.hasText(localRel, "Local relation must not be null or empty!");
try {
return messageSource == null ? null
: messageSource.getMessage(String.format(RELATION_MESSAGE_TEMPLATE, localRel));
} catch (NoSuchMessageException o_O) {
return null;
}
}
/*
* (non-Javadoc)
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
@@ -195,7 +244,7 @@ public class Jackson2HalModule extends SimpleModule {
@Override
public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
throws JsonMappingException {
return new HalLinkListSerializer(property, curieProvider, mapper);
return new HalLinkListSerializer(property, curieProvider, mapper, messageSource);
}
/*
@@ -611,18 +660,20 @@ public class Jackson2HalModule extends SimpleModule {
private final Map<Class<?>, Object> instanceMap = new HashMap<Class<?>, Object>();
public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider) {
this(resolver, curieProvider, true);
public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider,
MessageSourceAccessor messageSource) {
this(resolver, curieProvider, messageSource, true);
}
public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider,
boolean enforceEmbeddedCollections) {
MessageSourceAccessor messageSource, boolean enforceEmbeddedCollections) {
EmbeddedMapper mapper = new EmbeddedMapper(resolver, curieProvider, enforceEmbeddedCollections);
Assert.notNull(resolver, "RelProvider must not be null!");
this.instanceMap.put(HalResourcesSerializer.class, new HalResourcesSerializer(mapper));
this.instanceMap.put(HalLinkListSerializer.class, new HalLinkListSerializer(curieProvider, mapper));
this.instanceMap.put(HalLinkListSerializer.class,
new HalLinkListSerializer(curieProvider, mapper, messageSource));
}
private Object findInstance(Class<?> type) {
@@ -806,4 +857,25 @@ public class Jackson2HalModule extends SimpleModule {
return false;
}
}
static class HalLink {
private final Link link;
private final String title;
public HalLink(Link link, String title) {
this.link = link;
this.title = title;
}
@JsonUnwrapped
public Link getLink() {
return link;
}
@JsonInclude(Include.NON_NULL)
public String getTitle() {
return title;
}
}
}