diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/HalModelBuilder.java b/src/main/java/org/springframework/hateoas/mediatype/hal/HalModelBuilder.java index 2793780f..8d43e3e0 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/HalModelBuilder.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/HalModelBuilder.java @@ -359,7 +359,7 @@ public class HalModelBuilder { */ @SuppressWarnings("unchecked") public > RepresentationModel 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 extends EntityModel { private final @Nullable T entity; - private final CollectionModel embeddeds; + private final List embeddeds; - public HalRepresentationModel(@Nullable T entity, CollectionModel embeddeds, Links links) { + public HalRepresentationModel(@Nullable T entity, List embeddeds, Links links) { this(entity, embeddeds); add(links); } - private HalRepresentationModel(@Nullable T entity, CollectionModel embeddeds) { + private HalRepresentationModel(@Nullable T entity, List 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(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 add(Link link) { + HalRepresentationModel.this.add(link); + return this; + } + }; } } diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/HalModelBuilderUnitTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/HalModelBuilderUnitTest.java index 4bb4bc1a..4df71282 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/hal/HalModelBuilderUnitTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/hal/HalModelBuilderUnitTest.java @@ -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));