#1157 - Register custom serializers to handle Map based EntityModel instances.

We now register a custom serializer to massage EntityModel instances into dedicated types that – in case of a Map being the content of the entity model – wrap the model into a type that applies the necessary Jackson tweaks to properly unwrap a Map.
This commit is contained in:
Oliver Drotbohm
2019-12-01 21:47:44 +01:00
parent a084686156
commit 8cf23aa721
2 changed files with 91 additions and 0 deletions

View File

@@ -45,12 +45,14 @@ import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.*;
@@ -69,6 +71,7 @@ import com.fasterxml.jackson.databind.ser.ContainerSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.MapSerializer;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
/**
@@ -91,6 +94,8 @@ public class Jackson2HalModule extends SimpleModule {
setMixInAnnotation(Link.class, LinkMixin.class);
setMixInAnnotation(RepresentationModel.class, RepresentationModelMixin.class);
setMixInAnnotation(CollectionModel.class, CollectionModelMixin.class);
addSerializer(EntityModel.class, new EntityModelSerializer());
}
/**
@@ -105,6 +110,75 @@ public class Jackson2HalModule extends SimpleModule {
return LinkMixin.class.equals(mapper.findMixInClassFor(Link.class));
}
/**
* Custom serializer for {@link EntityModel} to make sure we get {@link Map} instances properly unwrapped. The
* serializer wraps the model instance into a dedicated classes that apply the proper Jackson configuration that's
* needed to achieve this and serializes those.
*
* @author Oliver Drotbohm
* @see https://github.com/FasterXML/jackson-databind/issues/171
*/
@SuppressWarnings("rawtypes")
static class EntityModelSerializer extends StdSerializer<EntityModel> {
private static final long serialVersionUID = -5933309398043585183L;
public EntityModelSerializer() {
super(EntityModel.class, false);
}
/*
* (non-Javadoc)
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
*/
@Override
@SuppressWarnings("null")
public void serialize(EntityModel value, JsonGenerator gen, SerializerProvider provider) throws IOException {
Object content = value.getContent();
RepresentationModel<?> wrapped = Map.class.isInstance(content) //
? new MapModel(value) //
: new NonMapModel(value);
provider.defaultSerializeValue(wrapped, gen);
}
static class MapModel extends RepresentationModel<MapModel> {
private final @Nullable Map<?, ?> map;
public MapModel(EntityModel<?> model) {
super(model.getLinks().toList());
this.map = (Map<?, ?>) model.getContent();
}
@Nullable
@JsonAnyGetter
public Map<?, ?> getContent() {
return map;
}
}
static class NonMapModel extends RepresentationModel<NonMapModel> {
private final EntityModel<?> model;
public NonMapModel(EntityModel<?> model) {
super(model.getLinks().toList());
this.model = model;
}
@Nullable
@JsonUnwrapped
public Object getContent() {
return model.getContent();
}
}
}
/**
* Custom {@link JsonSerializer} to render Link instances in HAL compatible JSON.
*