#1599 - Properly contextualize MapSuppressingUnwrappingSerializer.

According to FasterXML/jackson-databind#3272, we have to explicitly contextualize the delegate serializer we use for non-Map content in EntityModel.
This commit is contained in:
Oliver Drotbohm
2021-09-09 15:06:52 +02:00
parent 4705797322
commit cfd891ebf1
2 changed files with 51 additions and 2 deletions

View File

@@ -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<T> extends RepresentationModel<EntityModel<T>> {
return result;
}
private static class MapSuppressingUnwrappingSerializer extends StdSerializer<Object> {
private static class MapSuppressingUnwrappingSerializer extends StdSerializer<Object>
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<T> extends RepresentationModel<EntityModel<T>> {
* @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<T> extends RepresentationModel<EntityModel<T>> {
"@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<Object>) ((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()

View File

@@ -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 {
}
}