openapi springboot generator jackson 没有 String-argument constructor/factory 方法从 String 值反序列化
openapi springboot generator jackson no String-argument constructor/factory method to deserialize from String value
我正在使用 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
个字段,它会起作用。
知道这里出了什么问题吗?
OpenAPI Generator team implemented jackson-databind-nullable module which you should include to your project. The newest 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());
}
正如@Michał Ziober 所说,您必须将 jackson-databind-nullable 添加到您的 Maven 依赖项中,并且需要注册 jackson 模块。最简单的方法是将以下内容添加到您的应用程序中:
@Configuration
public class JacksonConfig {
@Bean
public Module jsonNullableModule() {
return new JsonNullableModule();
}
}
Spring 自动将 Jackson 模块添加到 ObjectMapper,如文档所述:
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.
从 openapi-generator v5.1.1 开始,您可以在 config.json:
中使用
{
"openApiNullable": false
}
删除该依赖项。大多数时候不需要它(如果你检查生成的代码),所以能够不在你的 class 路径中添加不需要的依赖是很酷的。
或者直接在 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 yaml 的 self-generated 代码):
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>0.2.1</version>
</dependency>
然后在@Configuration中class/es:
@Bean
public JsonNullableModule jsonNullableModule() {
return new JsonNullableModule();
}
和
@Bean
public RestTemplate restTemplate(ObjectMapper objectMapper) {
var converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
return new RestTemplateBuilder().additionalMessageConverters(converter).build();
}
这是唯一对我有用的解决方案,它不会破坏在 ObjectMapper
中添加的 post 序列化程序模块
@Configuration
public class WebApplicationConfig implements WebMvcConfigurer {
@Bean
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
return new MappingJackson2HttpMessageConverter(objectMapper());
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter());
converters.add(jackson2HttpMessageConverter());
converters.add(new ByteArrayHttpMessageConverter());
}
public ObjectMapper objectMapper() {
val mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
mapper.registerModule(new JsonNullableModule());
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new Jdk8Module());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
}
我正在使用 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
个字段,它会起作用。
知道这里出了什么问题吗?
OpenAPI Generator team implemented jackson-databind-nullable module which you should include to your project. The newest 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());
}
正如@Michał Ziober 所说,您必须将 jackson-databind-nullable 添加到您的 Maven 依赖项中,并且需要注册 jackson 模块。最简单的方法是将以下内容添加到您的应用程序中:
@Configuration
public class JacksonConfig {
@Bean
public Module jsonNullableModule() {
return new JsonNullableModule();
}
}
Spring 自动将 Jackson 模块添加到 ObjectMapper,如文档所述:
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.
从 openapi-generator v5.1.1 开始,您可以在 config.json:
中使用{
"openApiNullable": false
}
删除该依赖项。大多数时候不需要它(如果你检查生成的代码),所以能够不在你的 class 路径中添加不需要的依赖是很酷的。
或者直接在 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 yaml 的 self-generated 代码):
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>0.2.1</version>
</dependency>
然后在@Configuration中class/es:
@Bean
public JsonNullableModule jsonNullableModule() {
return new JsonNullableModule();
}
和
@Bean
public RestTemplate restTemplate(ObjectMapper objectMapper) {
var converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
return new RestTemplateBuilder().additionalMessageConverters(converter).build();
}
这是唯一对我有用的解决方案,它不会破坏在 ObjectMapper
中添加的 post 序列化程序模块@Configuration
public class WebApplicationConfig implements WebMvcConfigurer {
@Bean
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
return new MappingJackson2HttpMessageConverter(objectMapper());
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter());
converters.add(jackson2HttpMessageConverter());
converters.add(new ByteArrayHttpMessageConverter());
}
public ObjectMapper objectMapper() {
val mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
mapper.registerModule(new JsonNullableModule());
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new Jdk8Module());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
}