Skip to content

Commit

Permalink
#1157 - Fixed custom EntityModel serialization for Map payloads.
Browse files Browse the repository at this point in the history
Using a custom serializer seems to break downstream projects that also register serializers for EntityModel. We're now using extra methods on EntityModel itself that do the trick as well.
  • Loading branch information
odrotbohm committed Dec 12, 2019
1 parent 5502bfd commit ebe7038
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 74 deletions.
6 changes: 6 additions & 0 deletions src/main/java/org/springframework/hateoas/EntityModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonUnwrapped;

Expand Down Expand Up @@ -80,6 +81,11 @@ public T getContent() {

// Hacks to allow deserialization into an EntityModel<Map<String, Object>>

@JsonAnyGetter
private Map<String, Object> getMapContent() {
return Map.class.isInstance(content) ? (Map<String, Object>) content : null;
}

@JsonAnySetter
private void setPropertiesAsMap(String key, Object value) {
getOrInitAsMap().put(key, value);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -45,7 +45,6 @@
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;
Expand All @@ -70,7 +69,6 @@
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;

/**
Expand All @@ -93,8 +91,6 @@ public Jackson2HalModule() {
setMixInAnnotation(Link.class, LinkMixin.class);
setMixInAnnotation(RepresentationModel.class, RepresentationModelMixin.class);
setMixInAnnotation(CollectionModel.class, CollectionModelMixin.class);

addSerializer(EntityModel.class, new EntityModelSerializer());
}

/**
Expand All @@ -109,75 +105,6 @@ public static boolean isAlreadyRegisteredIn(ObjectMapper mapper) {
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.
*
Expand Down

0 comments on commit ebe7038

Please sign in to comment.