在 Spring REST 控制器中对 DateTime 使用 Jackson 反序列化
Use Jackson Deserialization for DateTime in Spring REST Controller
我在尝试将 POST 请求上传递给 Spring 控制器的值从 String 反序列化为 OffsetDateTime 时遇到此异常。
这是我的例外情况:
Failed to convert value of type 'java.lang.String' to required type 'java.time.OffsetDateTime';
nested exception is org.springframework.core.convert.ConversionFailedException:
Failed to convert from type [java.lang.String] to
type [@org.springframework.web.bind.annotation.RequestParam java.time.OffsetDateTime]
for value '2018-03-02T14:12:50.789+01:00';
nested exception is java.lang.IllegalArgumentException:
Parse attempt failed for value [2018-03-02T14:12:50.789+01:00]
我使用的是最新版本Spring-Boot - 2.0.1.BUILD-SNAPSHOT
这是我的JacksonConfig.java
package com.divinedragon.jackson.config;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static com.fasterxml.jackson.databind.DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL;
import static com.fasterxml.jackson.databind.PropertyNamingStrategy.SNAKE_CASE;
import static com.fasterxml.jackson.databind.SerializationFeature.WRAP_ROOT_VALUE;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
@Configuration
public class JacksonConfig {
@Bean(name = "jacksonConverter")
public MappingJackson2HttpMessageConverter jacksonConverter(final ObjectMapper objectMapper) {
final MappingJackson2HttpMessageConverter httpMessageConverter = new MappingJackson2HttpMessageConverter();
httpMessageConverter.setObjectMapper(objectMapper);
return httpMessageConverter;
}
@Bean
@Primary
public ObjectMapper objectMapper() {
final ObjectMapper mapper = new ObjectMapper();
mapper.enable(READ_UNKNOWN_ENUM_VALUES_AS_NULL);
mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES);
mapper.disable(WRAP_ROOT_VALUE);
mapper.setDateFormat(new ISO8601DateFormat());
mapper.setPropertyNamingStrategy(SNAKE_CASE);
mapper.registerModule(new Jdk8Module());
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new ParameterNamesModule());
return mapper;
}
}
这是我的 JacksonController.java
,它是一个 Spring REST 控制器。
package com.divinedragon.jackson.controller;
import java.time.OffsetDateTime;
import java.util.Collections;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class JacksonController {
@GetMapping(path = "/get")
public Map<String, OffsetDateTime> getDates() {
return Collections.singletonMap("createdAt", OffsetDateTime.now());
}
@PostMapping(path = "/post")
public Map<String, OffsetDateTime> postDates(@RequestParam("created_at") final OffsetDateTime createdAt) {
return Collections.singletonMap("createdAt", createdAt);
}
}
此应用程序运行,当我向 /get
端点发出请求时,我得到了使用 Jackson 正确序列化的日期值。
-> curl -s http://localhost:8080/get | python -m json.tool
{
"createdAt": "2018-03-02T14:12:50.789+01:00"
}
当我使用 /post
端点并传递日期值时,出现上述异常:
-> curl -s -X POST http://localhost:8080/post --data-urlencode 'created_at=2018-03-02T14:12:50.789+01:00' | python -m json.tool
{
"error": "Bad Request",
"message": "Failed to convert value of type 'java.lang.String' to required type 'java.time.OffsetDateTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam java.time.OffsetDateTime] for value '2018-03-02T14:12:50.789+01:00'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2018-03-02T14:12:50.789+01:00]",
"path": "/post",
"status": 400,
"timestamp": "2018-03-02T13:15:38Z"
}
有人可以指导我如何使用 Jackson 反序列化将值转换为 OffsetDateTime 吗?
似乎无法通过 @RequestParam
.
将 Jackson 截获从 String
转换为 OffsetDateTime
为了完成这项工作,我最终在 的帮助下编写了自己的转换器。
这是我的转换器:
@Component
public class CustomOffsetDateTimeConverter implements Converter<String, OffsetDateTime> {
@Autowired
private DateTimeFormatter dateTimeFormatter;
@Override
public OffsetDateTime convert(final String source) {
return OffsetDateTime.parse(source, dateTimeFormatter);
}
}
另外,为了让 Jackson 也符合相同的 DateTimeFormat
,我更新了我的 Jackson 配置。
我很快了解到,如果您想更新 Serialization/Deserialization 的格式,这是行不通的。
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
因此,我最终为此编写了自定义 Serializer/Deserializer,然后覆盖了 JavaTimeModule
的默认值。
这是我更新的 JacksonConfig.java
@Configuration
public class JacksonConfig {
@Bean("dateTimeFormatter")
public DateTimeFormatter dateTimeFormatter() {
return DateTimeFormatter.ofPattern(DATE_TIME_FORMAT_STRING);
}
@Bean
@Primary
public ObjectMapper objectMapper(final DateTimeFormatter dateTimeFormatter) {
final ObjectMapper mapper = new ObjectMapper();
mapper.enable(READ_UNKNOWN_ENUM_VALUES_AS_NULL);
mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES);
mapper.disable(WRAP_ROOT_VALUE);
mapper.disable(WRITE_DATES_AS_TIMESTAMPS);
mapper.setPropertyNamingStrategy(SNAKE_CASE);
final JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(OffsetDateTime.class, new CustomOffsetDateTimeSerializer(dateTimeFormatter));
javaTimeModule.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer(dateTimeFormatter));
mapper.registerModule(new Jdk8Module());
mapper.registerModule(javaTimeModule);
mapper.registerModule(new ParameterNamesModule());
return mapper;
}
@Bean(name = "jacksonConverter")
public MappingJackson2HttpMessageConverter jacksonConverter(final ObjectMapper objectMapper) {
final MappingJackson2HttpMessageConverter httpMessageConverter = new MappingJackson2HttpMessageConverter();
httpMessageConverter.setObjectMapper(objectMapper);
return httpMessageConverter;
}
}
class CustomOffsetDateTimeSerializer extends JsonSerializer<OffsetDateTime> {
private final DateTimeFormatter dateTimeFormatter;
public CustomOffsetDateTimeSerializer(final DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
@Override
public void serialize(final OffsetDateTime value, final JsonGenerator gen, final SerializerProvider serializers)
throws IOException {
gen.writeString(dateTimeFormatter.format(value));
}
}
@Component
class CustomOffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> {
private final DateTimeFormatter dateTimeFormatter;
public CustomOffsetDateTimeDeserializer(final DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
@Override
public OffsetDateTime deserialize(final JsonParser p, final DeserializationContext ctxt)
throws IOException, JsonProcessingException {
return OffsetDateTime.parse(p.getValueAsString(), dateTimeFormatter);
}
}
希望有一天这对某人有所帮助。
我在尝试将 POST 请求上传递给 Spring 控制器的值从 String 反序列化为 OffsetDateTime 时遇到此异常。
这是我的例外情况:
Failed to convert value of type 'java.lang.String' to required type 'java.time.OffsetDateTime';
nested exception is org.springframework.core.convert.ConversionFailedException:
Failed to convert from type [java.lang.String] to
type [@org.springframework.web.bind.annotation.RequestParam java.time.OffsetDateTime]
for value '2018-03-02T14:12:50.789+01:00';
nested exception is java.lang.IllegalArgumentException:
Parse attempt failed for value [2018-03-02T14:12:50.789+01:00]
我使用的是最新版本Spring-Boot - 2.0.1.BUILD-SNAPSHOT
这是我的JacksonConfig.java
package com.divinedragon.jackson.config;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static com.fasterxml.jackson.databind.DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL;
import static com.fasterxml.jackson.databind.PropertyNamingStrategy.SNAKE_CASE;
import static com.fasterxml.jackson.databind.SerializationFeature.WRAP_ROOT_VALUE;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
@Configuration
public class JacksonConfig {
@Bean(name = "jacksonConverter")
public MappingJackson2HttpMessageConverter jacksonConverter(final ObjectMapper objectMapper) {
final MappingJackson2HttpMessageConverter httpMessageConverter = new MappingJackson2HttpMessageConverter();
httpMessageConverter.setObjectMapper(objectMapper);
return httpMessageConverter;
}
@Bean
@Primary
public ObjectMapper objectMapper() {
final ObjectMapper mapper = new ObjectMapper();
mapper.enable(READ_UNKNOWN_ENUM_VALUES_AS_NULL);
mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES);
mapper.disable(WRAP_ROOT_VALUE);
mapper.setDateFormat(new ISO8601DateFormat());
mapper.setPropertyNamingStrategy(SNAKE_CASE);
mapper.registerModule(new Jdk8Module());
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new ParameterNamesModule());
return mapper;
}
}
这是我的 JacksonController.java
,它是一个 Spring REST 控制器。
package com.divinedragon.jackson.controller;
import java.time.OffsetDateTime;
import java.util.Collections;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class JacksonController {
@GetMapping(path = "/get")
public Map<String, OffsetDateTime> getDates() {
return Collections.singletonMap("createdAt", OffsetDateTime.now());
}
@PostMapping(path = "/post")
public Map<String, OffsetDateTime> postDates(@RequestParam("created_at") final OffsetDateTime createdAt) {
return Collections.singletonMap("createdAt", createdAt);
}
}
此应用程序运行,当我向 /get
端点发出请求时,我得到了使用 Jackson 正确序列化的日期值。
-> curl -s http://localhost:8080/get | python -m json.tool
{
"createdAt": "2018-03-02T14:12:50.789+01:00"
}
当我使用 /post
端点并传递日期值时,出现上述异常:
-> curl -s -X POST http://localhost:8080/post --data-urlencode 'created_at=2018-03-02T14:12:50.789+01:00' | python -m json.tool
{
"error": "Bad Request",
"message": "Failed to convert value of type 'java.lang.String' to required type 'java.time.OffsetDateTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam java.time.OffsetDateTime] for value '2018-03-02T14:12:50.789+01:00'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2018-03-02T14:12:50.789+01:00]",
"path": "/post",
"status": 400,
"timestamp": "2018-03-02T13:15:38Z"
}
有人可以指导我如何使用 Jackson 反序列化将值转换为 OffsetDateTime 吗?
似乎无法通过 @RequestParam
.
String
转换为 OffsetDateTime
为了完成这项工作,我最终在
这是我的转换器:
@Component
public class CustomOffsetDateTimeConverter implements Converter<String, OffsetDateTime> {
@Autowired
private DateTimeFormatter dateTimeFormatter;
@Override
public OffsetDateTime convert(final String source) {
return OffsetDateTime.parse(source, dateTimeFormatter);
}
}
另外,为了让 Jackson 也符合相同的 DateTimeFormat
,我更新了我的 Jackson 配置。
我很快了解到,如果您想更新 Serialization/Deserialization 的格式,这是行不通的。
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
因此,我最终为此编写了自定义 Serializer/Deserializer,然后覆盖了 JavaTimeModule
的默认值。
这是我更新的 JacksonConfig.java
@Configuration
public class JacksonConfig {
@Bean("dateTimeFormatter")
public DateTimeFormatter dateTimeFormatter() {
return DateTimeFormatter.ofPattern(DATE_TIME_FORMAT_STRING);
}
@Bean
@Primary
public ObjectMapper objectMapper(final DateTimeFormatter dateTimeFormatter) {
final ObjectMapper mapper = new ObjectMapper();
mapper.enable(READ_UNKNOWN_ENUM_VALUES_AS_NULL);
mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES);
mapper.disable(WRAP_ROOT_VALUE);
mapper.disable(WRITE_DATES_AS_TIMESTAMPS);
mapper.setPropertyNamingStrategy(SNAKE_CASE);
final JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(OffsetDateTime.class, new CustomOffsetDateTimeSerializer(dateTimeFormatter));
javaTimeModule.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer(dateTimeFormatter));
mapper.registerModule(new Jdk8Module());
mapper.registerModule(javaTimeModule);
mapper.registerModule(new ParameterNamesModule());
return mapper;
}
@Bean(name = "jacksonConverter")
public MappingJackson2HttpMessageConverter jacksonConverter(final ObjectMapper objectMapper) {
final MappingJackson2HttpMessageConverter httpMessageConverter = new MappingJackson2HttpMessageConverter();
httpMessageConverter.setObjectMapper(objectMapper);
return httpMessageConverter;
}
}
class CustomOffsetDateTimeSerializer extends JsonSerializer<OffsetDateTime> {
private final DateTimeFormatter dateTimeFormatter;
public CustomOffsetDateTimeSerializer(final DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
@Override
public void serialize(final OffsetDateTime value, final JsonGenerator gen, final SerializerProvider serializers)
throws IOException {
gen.writeString(dateTimeFormatter.format(value));
}
}
@Component
class CustomOffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> {
private final DateTimeFormatter dateTimeFormatter;
public CustomOffsetDateTimeDeserializer(final DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
@Override
public OffsetDateTime deserialize(final JsonParser p, final DeserializationContext ctxt)
throws IOException, JsonProcessingException {
return OffsetDateTime.parse(p.getValueAsString(), dateTimeFormatter);
}
}
希望有一天这对某人有所帮助。