spring-boot icon indicating copy to clipboard operation
spring-boot copied to clipboard

Spring Boot 4.0.0: InvalidDefinitionException when trying to deserialise an interface with TestRestTemplate

Open marco-brandizi opened this issue 3 weeks ago • 3 comments

I'm trying to migrate a project from Boot 3.5.8 to 4.0.0, but I'm having hard time making Boot to deserialise interfaces.

Please see the attached project, this is the tutorial (the complete/ project), modified to reproduce the use case at issue.

To summarise:

  • EntityController returns an Entity for a GET method. Entity is an Interface, the controller instantiates an implementing concrete class and returns it
  • Entity has a Metadata property attached. its getter returns this interface, EntityImpl.getMetadata() returns MetadataImpl
  • EntityControllerTest uses TestRestTemplate to call the GET URL and get an Entity, or an EntityImpl (I've tried both, with similar results)

What it gets instead is (when I use EntityImpl in restTemplate.exchange()):

[ERROR] com.example.restservice.EntityControllerTest.testBasics -- Time elapsed: 0.376 s <<< ERROR!
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.example.restservice.Metadata]
	at org.springframework.http.converter.AbstractJacksonHttpMessageConverter.readJavaType(AbstractJacksonHttpMessageConverter.java:363)
	at org.springframework.http.converter.AbstractJacksonHttpMessageConverter.read(AbstractJacksonHttpMessageConverter.java:322)
	at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:112)
	at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1035)
	at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1019)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:757)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:561)
	at org.springframework.boot.resttestclient.TestRestTemplate.exchange(TestRestTemplate.java:725)
	at com.example.restservice.EntityControllerTest.testBasics(EntityControllerTest.java:32)
Caused by: tools.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.restservice.Metadata` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); byte offset: #83] (through reference chain: com.example.restservice.EntityImpl["metadata"])
	at tools.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:70)
	at tools.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1958)
	at tools.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:448)
	at tools.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1488)
	at tools.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:254)
	at tools.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:552)
	at tools.jackson.databind.deser.bean.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:746)
	at tools.jackson.databind.deser.bean.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:642)
	at tools.jackson.databind.deser.bean.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1417)
	at tools.jackson.databind.deser.bean.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:480)
	at tools.jackson.databind.deser.bean.BeanDeserializer.deserialize(BeanDeserializer.java:200)
	at tools.jackson.databind.deser.DeserializationContextExt.readRootValue(DeserializationContextExt.java:265)
	at tools.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1646)
	at tools.jackson.databind.ObjectReader.readValue(ObjectReader.java:1171)
	at org.springframework.http.converter.AbstractJacksonHttpMessageConverter.readJavaType(AbstractJacksonHttpMessageConverter.java:355)
	... 9 more

And a similar error when I use Entity.class in restTemplate.exchange().

This happens despite @JsonCreator in both the constructors of EntityImpl and MetadataImpl, plus @JsonDeserialize ( contentAs = MetadataImpl.class ) in EntityImpl.getMetadata() or in the metadata parameter for EntityImpl's constructor (or in both).

This used to work well before Spring Boot 4. From the error message, I understand I should provide a mapping from abstract to concrete types, but so far, the mentioned annotations were the way to provide such a mapping. Is there some other mechanism now? Do I need to tell the TestRestTemplate to use the same annotations that the server is using? How?

Note that I don't want to place annotations like JsonDeserialize at the interface level, since this violates the Dependency Inversion Principle and would not work well when switching to an alternative implementation of the application data model.

Finally, I've tried the same with the rest test client and got the same error.

marco-brandizi avatar Nov 28 '25 20:11 marco-brandizi

OK, after struggling with CoPilot, I think I found a way:

// @JsonDeserialize no longer necessary

@Configuration
public class JacksonConfig
{
  @Bean
  public JacksonModule jacksonMappingsModule () 
  {
    SimpleModule module = new SimpleModule();
    module.addAbstractTypeMapping ( Metadata.class, MetadataImpl.class );
    return module;
  }
}

Which fixes the way Jackson 3.0.2 works. But it's ugly, maybe there is a way to re-enable the old behaviour?

marco-brandizi avatar Nov 29 '25 00:11 marco-brandizi

This used to work well before Spring Boot 4.

I am failing to see how you think this is a Spring Boot problem. From the description you're aware of the move to Jackson 3 and the impacts this may have. I've tried to illustrate that by adding the deprecated Jackson 2 module and configuring HTTP to use Jackson2 via spring.http.converters.preferred-json-mapper=jackson2 and the sample failed with:


org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.example.restservice.Metadata]

	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:405)
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:356)
	at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:103)
	at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1035)
	at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1019)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:757)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:561)
	at org.springframework.boot.resttestclient.TestRestTemplate.exchange(TestRestTemplate.java:725)
	at com.example.restservice.EntityControllerTest.testBasics(EntityControllerTest.java:32)
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.restservice.Metadata` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 84] (through reference chain: com.example.restservice.EntityImpl["metadata"])
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1943)
	at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:415)
	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1430)
	at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:273)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:553)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:597)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:490)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1499)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:340)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
	at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2146)
	at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1504)
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:397)
	... 9 more

I then downgraded your sample to Spring Boot 3.5.8 and it fails the same way.

The sample isn't actionable. If you think something in Spring Boot has to change, then please provide a sample that fails with 4.0 but does not with 3.5.

snicoll avatar Dec 01 '25 09:12 snicoll

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues avatar Dec 08 '25 09:12 spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

spring-projects-issues avatar Dec 15 '25 09:12 spring-projects-issues