Reputation: 930
After trying several variants, I'm stuck in trying to make my Spring HATEOAS controller do polymorphism.
My first variant was to implement the resources as instances of Resource
, with my object as content
. The base class is defined as follows:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type")
@JsonSubTypes({ @Type(value = Cat.class, name = "Cat"), @Type(value = Dog.class, name = "Dog") })
@JsonRootName("Animal")
public abstract class Animal
{
:
:
}
When fetching a single instance or a page, the following exception is thrown:
Could not write content: Unwrapped property requires use of type information: can not serialize without disabling `SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS`
The full repro is available here: https://github.com/Bert-R/spring-hateoas-polymorphism/tree/master/src/main/java/nu/famroos/spring/hateoas/polymorphism/repro1
My understanding is that this error is caused by an @JsonUnwrapped
annotation on the getContent
method of Resource
. The PagedResources
apparently uses a similar approach.
My second attempt was to make my classes inherit from ResourceSupport
, to circumvent the @JsonUnwrapped
issue. The base class now looks like this:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type")
@JsonSubTypes({ @Type(value = Cat2.class, name = "Cat2"), @Type(value = Dog2.class, name = "Dog2") })
@JsonRootName("Animal2")
public abstract class Animal2 extends ResourceSupport
{
:
:
}
This works partially. A single resource is now serialized correctly (note the @type
property):
{
"@type":"Dog2",
"name":"nameofdog",
"barkVolume":1.0,
"_links":{
"self":{
"href":"http://localhost:8082/animals2/nameofdog"
}
}
}
However, a page does not have the @type
property:
{
"_embedded":{
"Animal2s":[
{
"name":"nameofdog",
"barkVolume":1.0,
"_links":{
"self":{
"href":"http://localhost:8082/animals2/nameofdog"
}
}
},
:
:
]
},
"_links":{
"self":{
"href":"http://localhost:8082/animals2"
}
},
"page":{
"size":20,
"totalElements":3,
"totalPages":1,
"number":0
}
}
The code of this repro is available here: https://github.com/Bert-R/spring-hateoas-polymorphism/tree/master/src/main/java/nu/famroos/spring/hateoas/polymorphism/repro2
I would very much appreciate if someone could explain a way to make polymorphism work in combination with Spring HATEOAS.
Upvotes: 2
Views: 2088
Reputation: 930
After looking further, I found out the reason why the @type
attribute is not included in a page of resources: type erasure. See this issue.
There is a workaround for this: Instead of include = JsonTypeInfo.As.PROPERTY
, use include = JsonTypeInfo.As.EXISTING_PROPERTY
and add a property to each subclass that returns the type. It's not elegant, but it allows to implement a polymorphic API. The full source code for this workaround is available here: https://github.com/Bert-R/spring-hateoas-polymorphism/tree/master/src/main/java/nu/famroos/spring/hateoas/polymorphism/workaround
P.S. For those who feel that disabling SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS
would solve the issue in the first variant: if you disable that setting, the type information will be ignored, so the @type
attribute won't be added during serialization.
Upvotes: 3