LocalDateTime 字段:MismatchedInputException:无法从 START_OBJECT 令牌反序列化 `java.lang.String` 的实例

问题描述

我有一个主类:

@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class ContentModel implements Serializable {
    @JsonProperty("serviceData")
     private ServiceData serviceData;
}

和嵌套类:

 @NoArgsConstructor
 @AllArgsConstructor
 @Data
 @Builder
 public class ServiceData implements Serializable {
    @JsonProperty ("dateTimeMessageSend")
    private LocalDateTime dateTimeMessageSend;
 }

我正在尝试使用自定义反序列化器来反序列化:

public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
   private static final long serialVersionUID = 1L;
   protected LocalDateTimeDeserializer() {
        super(LocalDateTime.class);
    }

   @Override
   public LocalDateTime deserialize(final JsonParser jp,final DeserializationContext ctxt)
         throws IOException {
       return LocalDateTime.parse(jp.readValueAs(String.class));
    }
 }

这就是我如何配置我的 objectMapper:

  JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer());
    this.objectMapper.registerModule(javaTimeModule);
    this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    this.objectMapper.registerModule(new Jdk8Module());
    this.objectMapper.findAndRegisterModules();
 

应用程序在以下 json 之外接收:

{
 "serviceData": {
    "dateTimeMessageSend": {
       "month": "APRIL","dayOfYear": 109,"dayOfWeek": "MONDAY","nano": 183251000,"year": 2021,"monthValue": 4,"dayOfMonth": 19,"hour": 14,"minute": 52,"second": 44,"chronology": {
          "calendarType": "iso8601","id": "ISO"
         }
      },}
 }

在 LocalDateTime 反序列化时,我收到异常:

 MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token

我想,杰克逊将“dateTimeMessageSend”识别为一个对象,但无法将其解析为字符串。

解决问题的方法是什么?

  1. 修改源 json,例如将 LocalDateFormat 更改为 String。 (什么是不受欢迎的)
  2. 进一步自定义 objectMapper 或/和 LocalDateTimeDeserializer

解决方法

似乎对您实际收到的内容有些误解 在 deserialize 方法中,当 Jackson 调用您的 LocalDateTimeSerializer 时。

你不只是得到一个像 { "month": "APRIL","dayOfYear": 109,... }。 因此你不能简单地调用

jp.readValueAs(String.class);

这里有一个 MismatchedInputExcpetion,因为解析器 遇到 { 而不是字符串 "something"

实际上你会得到一个令牌流(以{开头) 您应该使用它直到结束 } 令牌, 从中提取相关部分, 并从这些部分构建一个 LocalDateTime 对象。

参见例如Baeldung - Getting started with deserialization in Jackson, 特别是那里给出的自定义解串器示例。 按照本教程,您可以像这样实现 deserialize 方法:

@Override
public LocalDateTime deserialize(final JsonParser jp,final DeserializationContext ctxt)
         throws IOException {
    JsonNode node = jp.getCodec().readTree(jp);
    int year = ((NumericNode) node.get("year")).asInt();
    int monthValue = ((NumericNode) node.get("monthValue")).asInt();
    int dayOfMonth = ((NumericNode) node.get("dayOfMonth")).asInt();
    int hour = ((NumericNode) node.get("hour")).asInt();
    int minute = ((NumericNode) node.get("minute")).asInt();
    int second = ((NumericNode) node.get("second")).asInt();
    return LocalDateTime.of(year,monthValue,dayOfMonth,hour,minute,second);
}

请注意,接收到的 JSON 树表示有相当多的冗余 LoclDateTime。例如:它包含 "month": "APRIL" and "monthValue": 4。因此,您无需提取所有 来自树的值,但只有其中一些。