#729 - Fix serialization of RepresentationModel for HAL-FORMS.
Original pull request: #824.
This commit is contained in:
committed by
Oliver Drotbohm
parent
8f14f03b97
commit
0a5e8b98e5
@@ -41,20 +41,20 @@ import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
*/
|
||||
class HalFormsDeserializers {
|
||||
|
||||
static class HalFormsResourcesDeserializer extends ContainerDeserializerBase<List<Object>>
|
||||
static class HalFormsCollectionModelDeserializer extends ContainerDeserializerBase<List<Object>>
|
||||
implements ContextualDeserializer {
|
||||
|
||||
private static final long serialVersionUID = -7325599536381465624L;
|
||||
|
||||
private JavaType contentType;
|
||||
|
||||
HalFormsResourcesDeserializer(JavaType contentType) {
|
||||
HalFormsCollectionModelDeserializer(JavaType contentType) {
|
||||
|
||||
super(contentType);
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
HalFormsResourcesDeserializer() {
|
||||
HalFormsCollectionModelDeserializer() {
|
||||
this(TypeFactory.defaultInstance().constructCollectionLikeType(List.class, Object.class));
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class HalFormsDeserializers {
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
return new HalFormsResourcesDeserializer(
|
||||
return new HalFormsCollectionModelDeserializer(
|
||||
property == null ? ctxt.getContextualType() : property.getType().getContentType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2017-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.
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.experimental.Wither;
|
||||
@@ -29,12 +30,15 @@ import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.PagedModel;
|
||||
import org.springframework.hateoas.PagedModel.PageMetadata;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.mediatype.PropertyUtils;
|
||||
import org.springframework.hateoas.mediatype.hal.HalLinkRelation;
|
||||
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.Jackson2HalFormsModule.HalFormsLinksDeserializer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
@@ -54,10 +58,15 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
@Value
|
||||
@Wither
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
@JsonPropertyOrder({ "resource", "resources", "embedded", "links", "templates", "metadata" })
|
||||
@JsonPropertyOrder({ "attributes", "resource", "resources", "embedded", "links", "templates", "metadata" })
|
||||
public class HalFormsDocument<T> {
|
||||
|
||||
@Nullable //
|
||||
@Getter(onMethod = @__(@JsonAnyGetter))
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
@Wither(AccessLevel.PRIVATE) //
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
@JsonUnwrapped //
|
||||
@JsonInclude(Include.NON_NULL) //
|
||||
private T resource;
|
||||
@@ -90,7 +99,21 @@ public class HalFormsDocument<T> {
|
||||
private Map<String, HalFormsTemplate> templates;
|
||||
|
||||
private HalFormsDocument() {
|
||||
this(null, null, Collections.emptyMap(), null, Links.NONE, Collections.emptyMap());
|
||||
this(null, null, null, Collections.emptyMap(), null, Links.NONE, Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link HalFormsDocument} for the given resource support.
|
||||
*
|
||||
* @param model can be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public static HalFormsDocument<?> forRepresentationModel(RepresentationModel<?> model) {
|
||||
|
||||
Map<String, Object> attributes = PropertyUtils.findProperties(model);
|
||||
attributes.remove("links");
|
||||
|
||||
return new HalFormsDocument<>().withAttributes(attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,11 +173,11 @@ public class HalFormsDocument<T> {
|
||||
}
|
||||
|
||||
public HalFormsDocument<T> withPageMetadata(@Nullable PageMetadata metadata) {
|
||||
return new HalFormsDocument<T>(resource, resources, embedded, metadata, links, templates);
|
||||
return new HalFormsDocument<T>(attributes, resource, resources, embedded, metadata, links, templates);
|
||||
}
|
||||
|
||||
private HalFormsDocument<T> withResource(@Nullable T resource) {
|
||||
return new HalFormsDocument<T>(resource, resources, embedded, pageMetadata, links, templates);
|
||||
return new HalFormsDocument<T>(attributes, resource, resources, embedded, pageMetadata, links, templates);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +190,7 @@ public class HalFormsDocument<T> {
|
||||
|
||||
Assert.notNull(link, "Link must not be null!");
|
||||
|
||||
return new HalFormsDocument<>(resource, resources, embedded, pageMetadata, links.and(link), templates);
|
||||
return new HalFormsDocument<>(attributes, resource, resources, embedded, pageMetadata, links.and(link), templates);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,7 +208,7 @@ public class HalFormsDocument<T> {
|
||||
Map<String, HalFormsTemplate> templates = new HashMap<>(this.templates);
|
||||
templates.put(name, template);
|
||||
|
||||
return new HalFormsDocument<>(resource, resources, embedded, pageMetadata, links, templates);
|
||||
return new HalFormsDocument<>(attributes, resource, resources, embedded, pageMetadata, links, templates);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,10 +226,6 @@ public class HalFormsDocument<T> {
|
||||
Map<HalLinkRelation, Object> embedded = new HashMap<>(this.embedded);
|
||||
embedded.put(key, value);
|
||||
|
||||
return new HalFormsDocument<>(resource, resources, embedded, pageMetadata, links, templates);
|
||||
}
|
||||
|
||||
HalFormsDocument<T> withLinks(Links links) {
|
||||
return new HalFormsDocument<>(resource, resources, embedded, pageMetadata, links, templates);
|
||||
return new HalFormsDocument<>(attributes, resource, resources, embedded, pageMetadata, links, templates);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2017-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.
|
||||
@@ -59,24 +59,94 @@ import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
*/
|
||||
class HalFormsSerializers {
|
||||
|
||||
static class HalFormsRepresentationModelSerializer extends ContainerSerializer<RepresentationModel<?>>
|
||||
implements ContextualSerializer {
|
||||
|
||||
private static final long serialVersionUID = -4583146321934407153L;
|
||||
|
||||
private final MessageSourceAccessor accessor;
|
||||
private final BeanProperty property;
|
||||
|
||||
HalFormsRepresentationModelSerializer(MessageSourceAccessor accessor, @Nullable BeanProperty property) {
|
||||
|
||||
super(RepresentationModel.class, false);
|
||||
this.property = property;
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
HalFormsRepresentationModelSerializer(MessageSourceAccessor accessor) {
|
||||
this(accessor, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (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(RepresentationModel<?> value, JsonGenerator gen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
|
||||
HalFormsDocument<?> doc = HalFormsDocument.forRepresentationModel(value) //
|
||||
.withLinks(value.getLinks()) //
|
||||
.withTemplates(findTemplates(value, accessor));
|
||||
|
||||
provider.findValueSerializer(HalFormsDocument.class, property).serialize(doc, gen, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(RepresentationModel<?> resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer typeSerializer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
|
||||
return new HalFormsRepresentationModelSerializer(accessor, property);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializer for {@link CollectionModel}.
|
||||
*/
|
||||
static class HalFormsResourceSerializer extends ContainerSerializer<EntityModel<?>> implements ContextualSerializer {
|
||||
static class HalFormsEntityModelSerializer extends ContainerSerializer<EntityModel<?>>
|
||||
implements ContextualSerializer {
|
||||
|
||||
private static final long serialVersionUID = -7912243216469101379L;
|
||||
|
||||
private final MessageSourceAccessor accessor;
|
||||
private final BeanProperty property;
|
||||
|
||||
HalFormsResourceSerializer(MessageSourceAccessor accessor, @Nullable BeanProperty property) {
|
||||
HalFormsEntityModelSerializer(MessageSourceAccessor accessor, @Nullable BeanProperty property) {
|
||||
|
||||
super(EntityModel.class, false);
|
||||
this.accessor = accessor;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
HalFormsResourceSerializer(MessageSourceAccessor accessor) {
|
||||
HalFormsEntityModelSerializer(MessageSourceAccessor accessor) {
|
||||
this(accessor, null);
|
||||
}
|
||||
|
||||
@@ -144,14 +214,14 @@ class HalFormsSerializers {
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new HalFormsResourceSerializer(accessor, property);
|
||||
return new HalFormsEntityModelSerializer(accessor, property);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializer for {@link CollectionModel}
|
||||
*/
|
||||
static class HalFormsResourcesSerializer extends ContainerSerializer<CollectionModel<?>>
|
||||
static class HalFormsCollectionModelSerializer extends ContainerSerializer<CollectionModel<?>>
|
||||
implements ContextualSerializer {
|
||||
|
||||
private static final long serialVersionUID = -3601146866067500734L;
|
||||
@@ -160,7 +230,7 @@ class HalFormsSerializers {
|
||||
private final Jackson2HalModule.EmbeddedMapper embeddedMapper;
|
||||
private final MessageSourceAccessor accessor;
|
||||
|
||||
HalFormsResourcesSerializer(MessageSourceAccessor accessor, @Nullable BeanProperty property,
|
||||
HalFormsCollectionModelSerializer(MessageSourceAccessor accessor, @Nullable BeanProperty property,
|
||||
Jackson2HalModule.EmbeddedMapper embeddedMapper) {
|
||||
|
||||
super(CollectionModel.class, false);
|
||||
@@ -170,7 +240,7 @@ class HalFormsSerializers {
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
HalFormsResourcesSerializer(MessageSourceAccessor accessor, Jackson2HalModule.EmbeddedMapper embeddedMapper) {
|
||||
HalFormsCollectionModelSerializer(MessageSourceAccessor accessor, Jackson2HalModule.EmbeddedMapper embeddedMapper) {
|
||||
this(accessor, null, embeddedMapper);
|
||||
}
|
||||
|
||||
@@ -254,7 +324,7 @@ class HalFormsSerializers {
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new HalFormsResourcesSerializer(accessor, property, embeddedMapper);
|
||||
return new HalFormsCollectionModelSerializer(accessor, property, embeddedMapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2018 the original author or authors.
|
||||
* Copyright 2016-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.
|
||||
@@ -34,10 +34,10 @@ import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalHandlerIns
|
||||
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListDeserializer;
|
||||
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer;
|
||||
import org.springframework.hateoas.mediatype.hal.LinkMixin;
|
||||
import org.springframework.hateoas.mediatype.hal.RepresentationModelMixin;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.HalFormsDeserializers.HalFormsResourcesDeserializer;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.HalFormsSerializers.HalFormsResourceSerializer;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.HalFormsSerializers.HalFormsResourcesSerializer;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.HalFormsDeserializers.HalFormsCollectionModelDeserializer;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.HalFormsSerializers.HalFormsCollectionModelSerializer;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.HalFormsSerializers.HalFormsEntityModelSerializer;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.HalFormsSerializers.HalFormsRepresentationModelSerializer;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.hateoas.server.mvc.JacksonSerializers.MediaTypeDeserializer;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -93,16 +93,19 @@ class Jackson2HalFormsModule extends SimpleModule {
|
||||
@JsonSerialize(using = HalLinkListSerializer.class)
|
||||
abstract class LinksMixin {}
|
||||
|
||||
@JsonSerialize(using = HalFormsResourceSerializer.class)
|
||||
@JsonSerialize(using = HalFormsRepresentationModelSerializer.class)
|
||||
abstract class RepresentationModelMixin extends org.springframework.hateoas.mediatype.hal.RepresentationModelMixin {}
|
||||
|
||||
@JsonSerialize(using = HalFormsEntityModelSerializer.class)
|
||||
abstract class EntityModelMixin<T> extends EntityModel<T> {}
|
||||
|
||||
@JsonSerialize(using = HalFormsResourcesSerializer.class)
|
||||
@JsonSerialize(using = HalFormsCollectionModelSerializer.class)
|
||||
abstract class CollectionModelMixin<T> extends CollectionModel<T> {
|
||||
|
||||
@Override
|
||||
@JsonProperty("_embedded")
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
@JsonDeserialize(using = HalFormsResourcesDeserializer.class)
|
||||
@JsonDeserialize(using = HalFormsCollectionModelDeserializer.class)
|
||||
public abstract Collection<T> getContent();
|
||||
}
|
||||
|
||||
@@ -166,8 +169,11 @@ class Jackson2HalFormsModule extends SimpleModule {
|
||||
|
||||
EmbeddedMapper mapper = new EmbeddedMapper(resolver, curieProvider, enforceEmbeddedCollections);
|
||||
|
||||
this.serializers.put(HalFormsResourceSerializer.class, new HalFormsResourceSerializer(accessor));
|
||||
this.serializers.put(HalFormsResourcesSerializer.class, new HalFormsResourcesSerializer(accessor, mapper));
|
||||
this.serializers.put(HalFormsRepresentationModelSerializer.class,
|
||||
new HalFormsRepresentationModelSerializer(accessor));
|
||||
this.serializers.put(HalFormsEntityModelSerializer.class, new HalFormsEntityModelSerializer(accessor));
|
||||
this.serializers.put(HalFormsCollectionModelSerializer.class,
|
||||
new HalFormsCollectionModelSerializer(accessor, mapper));
|
||||
this.serializers.put(HalLinkListSerializer.class,
|
||||
new HalLinkListSerializer(curieProvider, mapper, accessor, configuration.getHalConfiguration()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user