openapi springboot generator jackson no String-argument构造函数/工厂方法从字符串值反序列化

Posted

技术标签:

【中文标题】openapi springboot generator jackson no String-argument构造函数/工厂方法从字符串值反序列化【英文标题】:openapi springboot generator jackson no String-argument constructor/factory method to deserialize from String value 【发布时间】:2020-04-18 19:41:08 【问题描述】:

我正在使用OpenApi SpringBoot 生成器来生成控制器接口和模型。这将为可空字段创建带有JsonNullable<String> 的模型类。但是,我收到 Jackson 类型定义错误,而 POST 请求发送时的值存在于可空字段中。

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]

Integer 或任何其他类型也会发生同样的事情。如果请求仅包含 non-nullable 字段,则它可以工作。

知道这里出了什么问题吗?

【问题讨论】:

【参考方案1】:

OpenAPI Generator 团队实现了 jackson-databind-nullable 模块,您应该将其包含到您的项目中。最新的version is 0.2.1。

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

如果无法自动检测到模块,您需要手动执行:

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

或者如果你在Spring项目中使用Jackson,那么你可以通过以下方式注册:

@Bean
@Primary
public Jackson2ObjectMapperBuilder customObjectMapper() 
    return new Jackson2ObjectMapperBuilder()
            // other configs are possible
            .modules(new JsonNullableModule());

【讨论】:

谢谢!!!我在 @Configuration 类中注册了这样的模块。 @Autowired void configureObjectMapper(final ObjectMapper mapper) mapper.registerModule(new JsonNullableModule()); 没有解决我的问题:( spring boot 2.3.3 @m02ph3u5,您确定 JsonNullableModule 在您的情况下已正确注册吗? 我不这么认为。我为对象映射器添加了一个配置,但我认为生成的代码不会使用这个配置的 OM。还没有找到注入映射器的方法 @m02ph3u5 您找到解决方案了吗?我在注册JsonNullableModule 时遇到了同样的问题。【参考方案2】:

正如@Michał Ziober 所说,您必须将jackson-databind-nullable 添加到您的maven 依赖项中,并且需要注册jackson 模块。不过,最简单的方法是将以下内容添加到您的应用程序中:

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

Spring 自动将 Jackson 模块添加到 ObjectMapper,如文档所述:

com.fasterxml.jackson.databind.Module 类型的任何 bean 都是 使用自动配置自动注册 Jackson2ObjectMapperBuilder 并应用于任何 ObjectMapper 它创建的实例。这提供了一个全局机制 当您向您的项目添加新功能时贡献自定义模块 应用。

【讨论】:

【参考方案3】:

从 openapi-generator v5.1.1 开始,您可以在 config.json 中使用:


  "openApiNullable": false

删除该依赖项。大多数时候它是不需要的(如果你检查生成的代码),所以能够不在你的类路径中添加不需要的依赖是很酷的。

或者直接在你build.gradle的gradle插件中你可以这样做:

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"
    ]

【讨论】:

以上是关于openapi springboot generator jackson no String-argument构造函数/工厂方法从字符串值反序列化的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring Boot 中使用 Gradle 从 Swagger OpenAPI 生成 RestClient 存根

SpringDoc openAPI 工具未扫描 Spring Boot 项目中的 API

springdoc-openapi 在没有服务器的情况下生成 openapi yaml

使用泛型继承的springdoc-openapi规范生成

SpringBoot整合Swagger测试api构建

使用 springdoc-openapi 和 spring-boot-starter-data-mongodb 生成 OpenAPI 文档