Why do I get warnings when using Spring HATEOAS Jackson serializers
Asked Answered
N

1

1

Spring HATEOAS defines and registers a HttpMessageConverter with a Jackson module that contains serializers to transform its ResourceSupport and Resources types to HAL JSON representations. The modules uses Jackson mixins to bind the serializers to types like so:

package org.springframework.hateoas.hal;

// Imports omitted

public abstract class ResourcesMixin<T> extends Resources<T> {

    @Override
    @XmlElement(name = "embedded")
    @JsonProperty("_embedded")
    @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, using = Jackson2HalModule.HalResourcesSerializer.class)
    @JsonDeserialize(using = Jackson2HalModule.HalResourcesDeserializer.class)
    public abstract Collection<T> getContent();    
}

This mixin causes types that extend Resources<T> to get serialized and deserialized using Jackson2HalModule.HalResourcesSerializer.class. However, the implementation seems limited in its ability to embed resources. In trying to address this limitation, I tried using annotations from the above Jackson mixin to use the Spring HATEOAS serializers in my own class:

package mypackage;

// Imports omitted

public class ModelAResource extends Resource<ModelA> {

    private Collection<Resource<ModelB>> modelBResources;

    public ModelAResource(Model model, Collection<Resource<ModelB>> modelBResources) {
        super(model);
        this.modelBResources = modelBResources;
    }

    @JsonProperty("_embedded")
    @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, using = Jackson2HalModule.HalResourcesSerializer.class)
    @JsonDeserialize(using = Jackson2HalModule.HalResourcesDeserializer.class)
    public Collection<Resource<ModelB>> getModelBResources() {
        return modelBResources;
    }
}

This works, but leads to the following WARNs getting logged:

[2015-01-27 23:36:41.469] boot - 12516  WARN [qtp1175418534-17] ---
MappingJackson2HttpMessageConverter: Failed to evaluate serialization for type
[class mypackage.ModelAResource]:
org.springframework.beans.factory.BeanCreationException: Error creating bean 
with name
'org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer':
Instantiation of bean failed; nested exception is 
org.springframework.beans.BeanInstantiationException: Failed to instantiate
[org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer]: No
default constructor found; nested exception is
java.lang.NoSuchMethodException:
org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer<init>()

[2015-01-27 23:36:41.478] boot - 12516  WARN [qtp1175418534-17] ---
MappingJackson2HttpMessageConverter: Failed to evaluate serialization for type
[class mypackage.ModelAResource]:
com.fasterxml.jackson.databind.JsonMappingException: Class
org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer has no 
default (no arg) constructor 

Despite these warnings, serialization yields the expected, desired result:

{
  "id" : 6,
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/modelA/6"
    },
    "links" : {
      "href" : "http://localhost:8080/modelA/6/modelBs"
    }
  },
  "_embedded" : {
    "modelBs" : [ {
      "_links": {
        "self": {
          "href": "http://localhost:8080/modelA/6/modelBs/1"
        }
      }, 
      "id" : 1
    } ]
  }
}

So why the warnings, and why does it work despite the warnings? The module and serializer are public.

Nip answered 28/1, 2015 at 0:23 Comment(0)
M
4

When you use @JsonDeserialize(using = Jackson2HalModule.HalResourcesDeserializer.class), then Jackson tries to create an instance of Jackson2HalModule.HalResourcesSerializer, which doesn't work because it has no default constructor.

Spring HATEOAS (later) directly registers some Jackson stuff, including a Jackson2HalModule.HalResourcesSerializer. Now serialization works as expected because an instance of the serializer has successfully been registered.

Marras answered 28/1, 2015 at 10:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.