diff --git a/src/main/java/org/springframework/hateoas/EntityModel.java b/src/main/java/org/springframework/hateoas/EntityModel.java index 8724d710..4527a29c 100644 --- a/src/main/java/org/springframework/hateoas/EntityModel.java +++ b/src/main/java/org/springframework/hateoas/EntityModel.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -29,9 +30,12 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.std.JsonValueSerializer; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.databind.util.NameTransformer; @@ -197,10 +201,23 @@ public class EntityModel extends RepresentationModel> { return result; } - private static class MapSuppressingUnwrappingSerializer extends StdSerializer { + private static class MapSuppressingUnwrappingSerializer extends StdSerializer + implements ContextualSerializer { + private static final long serialVersionUID = -8367255762553946324L; + + private final @Nullable BeanProperty property; + + @SuppressWarnings("unused") public MapSuppressingUnwrappingSerializer() { + this(null); + } + + private MapSuppressingUnwrappingSerializer(@Nullable BeanProperty property) { + super(Object.class); + + this.property = property; } /* @@ -208,7 +225,9 @@ public class EntityModel extends RepresentationModel> { * @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) */ @Override - public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { + @SuppressWarnings({ "null", "unchecked" }) + public void serialize(@Nullable Object value, @Nullable JsonGenerator gen, @NonNull SerializerProvider provider) + throws IOException { if (value == null || Map.class.isInstance(value)) { return; @@ -221,11 +240,25 @@ public class EntityModel extends RepresentationModel> { "@JsonValue rendered classes can not be directly nested in EntityModel as they do not produce a document key!"); } + if (ContextualSerializer.class.isInstance(serializer)) { + serializer = (JsonSerializer) ((ContextualSerializer) serializer).createContextual(provider, property); + } + serializer // .unwrappingSerializer(NameTransformer.NOP) // .serialize(value, gen, provider); } + /* + * (non-Javadoc) + * @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty) + */ + @Override + public JsonSerializer createContextual(@Nullable SerializerProvider prov, @Nullable BeanProperty property) + throws JsonMappingException { + return new MapSuppressingUnwrappingSerializer(property); + } + /* * (non-Javadoc) * @see com.fasterxml.jackson.databind.JsonSerializer#isUnwrappingSerializer() diff --git a/src/test/java/org/springframework/hateoas/EntityModelUnitTest.java b/src/test/java/org/springframework/hateoas/EntityModelUnitTest.java index ee04c63f..1d952ebd 100755 --- a/src/test/java/org/springframework/hateoas/EntityModelUnitTest.java +++ b/src/test/java/org/springframework/hateoas/EntityModelUnitTest.java @@ -21,7 +21,9 @@ import java.util.Collections; import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -88,9 +90,23 @@ class EntityModelUnitTest { .withMessageContaining("@JsonValue"); } + @Test // #1371 + void rendersTypeIdentifiersCorrectly() throws Exception { + + EntityModel model = EntityModel.of(new TypeWithId()); + + assertThatNoException() + .isThrownBy(() -> new ObjectMapper().writeValueAsString(model)); + } + // #1371 static class ValueType { @JsonValue String type; } + + @JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class, property = "@jsonObjectId") + static class TypeWithId { + + } }