#1540 - Avoid double curie-ing of HalModel embeds.
Embedded elements assembled through a HalModelBuilder expose those through a CollectionModel to make sure that the individual elements get grouped by their link relation and those potentially curied. To indicate a curie link needing to be added because of curied embeds, the serialization adds a magic link to the list of links of that CollectionModel. As the latter is unwrapped, an additiona _links block rendering a curie had been added to the representation rendering the JSON representation invalid, as it now contained two _links fields (the latter stemming from the actual links added to the HAL representation model). We now tweak the CollectionModel returned to override ….add(Link) and rather route the magic link to the outer representation model, causing the inner one to never accumulate any links in the first place and thus not render an invalid _links.
This commit is contained in:
@@ -359,7 +359,7 @@ public class HalModelBuilder {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends RepresentationModel<T>> RepresentationModel<T> build() {
|
||||
return (T) new HalRepresentationModel<>(model, CollectionModel.of(embeddeds), links);
|
||||
return (T) new HalRepresentationModel<>(model, embeddeds, links);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,16 +381,16 @@ public class HalModelBuilder {
|
||||
private static class HalRepresentationModel<T> extends EntityModel<T> {
|
||||
|
||||
private final @Nullable T entity;
|
||||
private final CollectionModel<?> embeddeds;
|
||||
private final List<Object> embeddeds;
|
||||
|
||||
public HalRepresentationModel(@Nullable T entity, CollectionModel<T> embeddeds, Links links) {
|
||||
public HalRepresentationModel(@Nullable T entity, List<Object> embeddeds, Links links) {
|
||||
|
||||
this(entity, embeddeds);
|
||||
|
||||
add(links);
|
||||
}
|
||||
|
||||
private HalRepresentationModel(@Nullable T entity, CollectionModel<?> embeddeds) {
|
||||
private HalRepresentationModel(@Nullable T entity, List<Object> embeddeds) {
|
||||
|
||||
Assert.notNull(embeddeds, "Embedds must not be null!");
|
||||
|
||||
@@ -409,8 +409,21 @@ public class HalModelBuilder {
|
||||
}
|
||||
|
||||
@JsonUnwrapped
|
||||
@SuppressWarnings("deprecation")
|
||||
public CollectionModel<?> getEmbeddeds() {
|
||||
return embeddeds;
|
||||
|
||||
return new CollectionModel<Object>(embeddeds) {
|
||||
|
||||
/**
|
||||
* Overriding this to make sure that the marker link added to signal the need for curie-ing is added to the
|
||||
* outer representation model.
|
||||
*/
|
||||
@Override
|
||||
public CollectionModel<Object> add(Link link) {
|
||||
HalRepresentationModel.this.add(link);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,10 @@ import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.MappingTestUtils.ContextualMapper;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.UriTemplate;
|
||||
import org.springframework.hateoas.mediatype.MessageResolver;
|
||||
import org.springframework.hateoas.server.core.EvoInflectorLinkRelationProvider;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
@@ -360,6 +362,22 @@ public class HalModelBuilderUnitTest {
|
||||
assertEmptyEmbed(halModel().embed(Stream.empty(), LinkRelation.of("products")).build(), "products");
|
||||
}
|
||||
|
||||
@Test // #1540
|
||||
void doesNotRenderAdditionalLinksBlockForCuriedEmbeds() throws Exception {
|
||||
|
||||
this.mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator( //
|
||||
new EvoInflectorLinkRelationProvider(), //
|
||||
new DefaultCurieProvider("foo", UriTemplate.of("http://localhost/foo/{rel}")), //
|
||||
MessageResolver.DEFAULTS_ONLY));
|
||||
|
||||
RepresentationModel<?> model = halModel() //
|
||||
.embed(new Staff("Frodo Baggins", "ring bearer")) //
|
||||
.build();
|
||||
|
||||
assertThat(StringUtils.countOccurrencesOf(mapper.writeValueAsString(model), "_links")) //
|
||||
.isEqualTo(1);
|
||||
}
|
||||
|
||||
private void assertEmptyEmbed(RepresentationModel<?> model, String name) throws Exception {
|
||||
|
||||
DocumentContext context = JsonPath.parse(mapper.writeValueAsString(model));
|
||||
|
||||
Reference in New Issue
Block a user