Spring 控制器不会即时反序列化

问题描述 投票:0回答:1

我在将 Spring 与 Jackson 集成时遇到问题。

我有一个 POJO,其中有一些带有自定义日期格式的

Instant
字段:

public class C{
   @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss", timezone = "UTC")
   private Instant postedAtFrom;
   @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss", timezone = "UTC")
   private Instant postedAtTo;
}

然后我尝试使用这个 POJO 作为其余 GET 映射的查询参数:

@RestController
@RequestMapping("/c")
@RequiredArgsConstructor
@Slf4j
public class AnnounceController {

   @Autowired
   private final ObjectMapper objectMapper;

   @PostConstruct
   public void test() throws JsonProcessingException {

      System.out.println(objectMapper.readValue("{\"postedAtFrom\":\"20-05-2022 15:50:07\"}", C.class));

   }

   @GetMapping
   public BasicResponse<Page<Announce>> get(final C filter, @NonNull final Pageable page) {
      return   new BasicResponse<>(service.get(filter, page));
   }
   
}

PostConstruct
代码工作正常,它打印的正是它应该打印的内容,但是每当我尝试使用相同的请求访问该端点时:

CURL
    GET /c?postedAtFrom=20-05-2022 15:50:07 HTTP/1.1
    Host: localhost 

我得到这个例外:

org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'filter' on field 'postedAtFrom': rejected value [20-05-2022 15:50:07]
codes [typeMismatch.filter.postedAtFrom,typeMismatch.postedAtFrom,typeMismatch.java.time.Instant,typeMismatch]
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [filter.postedAtFrom,postedAtFrom]
arguments []
default message [postedAtFrom]]
default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.Instant' for property 'postedAtFrom'
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@com.fasterxml.jackson.annotation.JsonFormat java.time.Instant] for value [20-05-2022 15:50:07]
nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [20-05-2022 15:50:07]]

这是

ObjectMapper
bean 的代码:

@Bean
   @Primary
   public ObjectMapper configure(@NonNull final ObjectMapper objectMapper) {
      final SimpleModule javaTimeModule = new JavaTimeModule()
              .addDeserializer(Instant.class, InstantDeserializer.INSTANT)
              .addSerializer(Instant.class, InstantSerializer.INSTANCE);
      return objectMapper.registerModule(javaTimeModule);

   }

我想使用 Spring 和 Jackson 设计的工具来解决这个问题。请不要建议“将其转换为字符串”或“发帖”等解决方法。

java spring jackson objectmapper
1个回答
0
投票

正如其他人在评论中所说,目前还不清楚您期望发生什么。我在下面做出假设。

考虑到

@JsonFormat
注释和自定义
ObjectMapper
,如果您期望 Jackson 在这里介入并解析 JSON,那是不会发生的。需要明确的是,这里的任何地方都不涉及 Jackson 或 JSON 解析。

您的

final C filter
处理程序方法参数未使用
@RequestBody
之类的内容进行注释(并且您没有在 GET Curl 调用中发送请求正文)。如果没有注释,Spring MVC 会将参数作为模型属性处理,并使用
ModelAttributeMethodProcessor
进行处理。

它会创建您类型的实例

C
并尝试使用查询(或表单 - 如果您使用过)参数填充其字段。在这种情况下,它将查找名为
postedAtFrom
的查询参数来匹配该字段(实际上是 bean/pojo 访问器,我也假设您有)。

处理器使用一组 Converter 实例将字符串值(查询参数)中的字段填充到模型属性类型可能使用的复杂类型中。

Spring Boot 维护自己的 default

Converter
实例列表。其中有一个用于
java.time
类型的
Jsr310DateTimeFormatAnnotationFormatterFactory
,它被设置为处理
String
Instant
的转换。

但是,您的查询参数值

20-05-2022 15:50:07
不会被解析为
Instant
,有关于
Instant
缺失的信息(时区)。在某些情况下,您可以注释您的字段
@DateTimeFormat
以指定自定义格式,但我认为这在这里不起作用。

您可以将您的字段更改为

LocalDateTime

@DateTimeFormat(pattern = "dd-MM-yyyy HH:mm:ss")
private LocalDateTime postedAtFrom;

使用与您的输入相匹配的适当模式。并且,每当您需要

Instant
时,请使用
LocalDateTime
toInstant
方法和适当的
ZoneOffset

或者,您可以使用自己的

Converter
实例列表来配置 Spring Boot,这些实例可以使用您自己的
Instant
直接解析为
DateTimeFormatter
。有关如何执行此操作的信息,请参阅这篇文章

© www.soinside.com 2019 - 2024. All rights reserved.