Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

openapi springboot generator jackson no String-argument constructor/factory method to deserialize from String value

I'm using OpenApi SpringBoot generator to generate controller interfaces and models. This creates model classes with JsonNullable<String> for nullable fields. However I'm getting a Jackson type definition error while POST request is sent with value present in a nullable field.

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.openapitools.jackson.nullable.JsonNullable` (no Creators, like default construct, exist): no String-argument constructor/factory method to deserialize from String value ('TG')
 at [Source: (PushbackInputStream); line: 3, column: 19] (through reference chain: com.example.rest.CreateRequest["displayName"])
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1028) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013) ~[jackson-databind-2.9.7.jar:2.9.7]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084) ~[jackson-databind-2.9.7.jar:2.9.7]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:239) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:227) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:204) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:157) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:130) ~[spring-webmvc-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:126) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]

Same thing happens for Integer or any other type. It works if the request contains only non-nullable fields.

Any idea what is going wrong here ?

like image 272
Dhanuj Dharmarajan Avatar asked Dec 30 '19 00:12

Dhanuj Dharmarajan


3 Answers

Since openapi-generator v5.1.1 you can use in your config.json:

{
  "openApiNullable": false
}

To remove that dependency. Most of the time it's not needed (if you check the generated code), so it's cool to be able to not add an unwanted dependency in your class path.

Or directly in the gradle plugin in your build.gradle you can do:

openApiGenerate {
    generatorName = "spring"
    inputSpec = "$rootDir/specs/petstore-v3.0.yaml"
    outputDir = "$buildDir/generated"
    apiPackage = "org.openapi.example.api"
    modelPackage = "org.openapi.example.model"
    configOptions = [
        openApiNullable: "false"
    ]
}
like image 58
Sylhare Avatar answered Oct 28 '22 02:10

Sylhare


OpenAPI Generator team implemented jackson-databind-nullable module which you should include to your project. The newest version is 0.2.3.

<dependency>
    <groupId>org.openapitools</groupId>
    <artifactId>jackson-databind-nullable</artifactId>
    <version>0.2.3</version>
</dependency>

If module will not be detected automatically you need to do it manually by:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JsonNullableModule());

Or if you using Jackson in Spring project, then you can register it by:

@Bean
@Primary
public Jackson2ObjectMapperBuilder customObjectMapper() {
    return new Jackson2ObjectMapperBuilder()
            // other configs are possible
            .modules(new JsonNullableModule());
}
like image 35
Michał Ziober Avatar answered Oct 28 '22 04:10

Michał Ziober


As @Michał Ziober said, you have to add jackson-databind-nullable to your maven dependencies and need to register the jackson module. The easiest way though is to add the following to your application:

@Configuration
public class JacksonConfig {
  @Bean
  public Module jsonNullableModule() {
    return new JsonNullableModule();
  }
}

Spring automatically adds Jackson Modules to the ObjectMapper, as the docs say:

Any beans of type com.fasterxml.jackson.databind.Module are automatically registered with the auto-configured Jackson2ObjectMapperBuilder and are applied to any ObjectMapper instances that it creates. This provides a global mechanism for contributing custom modules when you add new features to your application.

like image 34
Fzum Avatar answered Oct 28 '22 03:10

Fzum