-
Notifications
You must be signed in to change notification settings - Fork 41.7k
Description
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:
EntityControllerreturns anEntityfor a GET method.Entityis an Interface, the controller instantiates an implementing concrete class and returns it- Entity has a
Metadataproperty attached. its getter returns this interface,EntityImpl.getMetadata()returnsMetadataImpl EntityControllerTestusesTestRestTemplateto call the GET URL and get anEntity, or anEntityImpl(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.