OpenAPITools / OpenAPITools/openapi-generator
[BUG][SPRING] oneOf type: Jackson trying to instantiate interface instead of implementation
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Description
I'm using the oneOf feature to define several possible schemas that can go into a response body of my service. In the generated Java client code, the Java implementations of these schemas implement an interface, but when I send a request through, Jackson is trying to create an instance of the interface, instead of the concrete class.
The same issue was reported and fixed in swagger-codegen project: https://github.com/swagger-api/swagger-codegen/issues/10011
openapi-generator version
6.4.0
OpenAPI declaration file content or url
openapi: 3.0.1
info:
version: '2.0'
title: ABC API documentation
...
responses:
...
...
CancellationDuration:
type: object
oneOf:
- $ref: '#/components/schemas/DeadlineAbsolute'
- $ref: '#/components/schemas/DeadlineDurationBeforeArrival'
Generation Details
openapi-generator-maven-plugin is used, full configuration:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>6.4.0</version>
<configuration>
<generateSupportingFiles>true</generateSupportingFiles>
<generatorName>spring</generatorName>
<generateApiDocumentation>true</generateApiDocumentation>
<generateModelDocumentation>true</generateModelDocumentation>
<configOptions>
<oas3>true</oas3>
<useTags>true</useTags>
<withSeparateModelsAndApi>true</withSeparateModelsAndApi>
<delegatePattern>true</delegatePattern>
<withInterfaces>true</withInterfaces>
<library>spring-boot</library>
<dateLibrary>java8</dateLibrary>
<useSpringfox>false</useSpringfox>
<useSpringController>true</useSpringController>
<modelPropertyNaming>camelCase</modelPropertyNaming>
</configOptions>
<!-- Option to prevent openapi generator to strip off common prefix from enum values -->
<additionalProperties>removeEnumValuePrefix=false</additionalProperties>
<!-- <skipIfSpecIsUnchanged>true</skipIfSpecIsUnchanged>-->
</configuration>
<executions>
<execution>
<id>spring-execution</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<ignoreFileOverride>${project.basedir}/.openapi-generator-ignore</ignoreFileOverride>
<inputSpec>${project.basedir}/src/main/resources/yaml/apis/api/Api.yaml</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>com.mycompany.obt.master.apis</apiPackage>
<modelPackage>com.mycompany.obt.master.models</modelPackage>
</configuration>
</execution>
<execution>
<id>typescript-execution</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/yaml/apis/api/Api.yaml</inputSpec>
<generatorName>typescript-axios</generatorName>
<apiPackage>com.mycompany.obt.master.apis</apiPackage>
<modelPackage>com.mycompany.obt.master.models</modelPackage>
</configuration>
</execution>
</executions>
</plugin>
This results in an empty interface
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2023-03-31T09:08:52.189665+05:30[Asia/Kolkata]")
public interface CancellationDuration {}
And two concrete classes:
@Schema(name = "DeadlineAbsolute", description = "Absolute Date and time before which cancellation policy is applicable")
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2023-03-31T09:13:38.604952+05:30[Asia/Kolkata]")
public class DeadlineAbsolute implements CancellationDuration { ... }
@Schema(name = "DeadlineDurationBeforeArrival", description = "Time before arrival when cancellation policy is applicable")
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2023-03-31T09:13:38.604952+05:30[Asia/Kolkata]")
public class DeadlineDurationBeforeArrival implements CancellationDuration { ... }
This error occurs on deserialization:
Caused by: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.mycompany.obt.master.models.CancellationDuration]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.mycompany.obt.master.models.CancellationDuration` (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: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1762, column: 32]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:388)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:343)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:185)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:160)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:133)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the generated Spring models from the provided OpenAPI declaration and Maven plugin configuration, focusing on CancellationDuration, DeadlineAbsolute, and DeadlineDurationBeforeArrival. Then trace how the Spring generator represents oneOf during Jackson deserialization; done means a response containing either schema deserializes to the appropriate concrete class instead of the interface.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, openapi, spring
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100