diff --git a/src/main/java/org/springframework/hateoas/EntityModel.java b/src/main/java/org/springframework/hateoas/EntityModel.java index b970f583..24618caf 100644 --- a/src/main/java/org/springframework/hateoas/EntityModel.java +++ b/src/main/java/org/springframework/hateoas/EntityModel.java @@ -21,6 +21,8 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -117,7 +119,7 @@ public class EntityModel extends RepresentationModel> { @JsonUnwrapped @Nullable public T getContent() { - return content; + return !Map.class.isInstance(content) ? content : null; } // Hacks to allow deserialization into an EntityModel> @@ -125,7 +127,7 @@ public class EntityModel extends RepresentationModel> { @Nullable @JsonAnyGetter @SuppressWarnings("unchecked") - private Map getMapContent() { + public Map getMapContent() { return Map.class.isInstance(content) ? (Map) content : null; } diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/Jackson2HalIntegrationTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/Jackson2HalIntegrationTest.java index 15f7e8bb..18c4668b 100755 --- a/src/test/java/org/springframework/hateoas/mediatype/hal/Jackson2HalIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/hal/Jackson2HalIntegrationTest.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.TreeMap; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; @@ -591,7 +592,8 @@ class Jackson2HalIntegrationTest { EntityModel> result = mapper.readValue(source, modelType); - assertThat(result.getContent()).containsEntry("key", "value"); + assertThat(result.getContent()).isNull(); + assertThat(result.getMapContent()).containsEntry("key", "value"); } @Test // #1157 @@ -606,6 +608,22 @@ class Jackson2HalIntegrationTest { assertThat(result.getContent().name).isEqualTo("Dave"); } + @Test // #1352 + void rendersMapWithoutDuplicateEntries() throws JsonProcessingException { + + Map map = new TreeMap<>(); + map.put("key", "value"); + map.put("key2", "value2"); + + EntityModel> entityModel = EntityModel.of(map); + entityModel.add(Link.of("http://example.com")); + + String serialized = mapper.writeValueAsString(entityModel); + + assertThat(serialized) + .isEqualTo("{\"_links\":{\"self\":{\"href\":\"http://example.com\"}},\"key\":\"value\",\"key2\":\"value2\"}"); + } + @Relation(collectionRelation = "someSample") static class SomeSample { @JsonProperty String name; diff --git a/src/test/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt b/src/test/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt index 200d66a2..8af66e99 100644 --- a/src/test/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt +++ b/src/test/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt @@ -64,8 +64,10 @@ class ReactiveRepresentationModelAssemblerBuilderDslUnitTest : TestUtils() { runBlocking { val collectionModel = testResourceAssembler.toCollectionModelAndAwait(employees, exchange) - assertThat(collectionModel.content.flatMap { entityModel -> entityModel.links }) - .containsExactlyInAnyOrder(Link.of("/employees", EMPLOYEES_RELATION), Link.of("/employees", EMPLOYEES_RELATION)) + val extractLinks: (EntityModel) -> Iterable = { entityModel -> entityModel.links } + + assertThat(collectionModel.content.flatMap(extractLinks)) + .containsExactlyInAnyOrder(Link.of("/employees", EMPLOYEES_RELATION), Link.of("/employees", EMPLOYEES_RELATION)) } }