Jackson LocalDateTime的反序列化问题


当我们使用java8的LocalDateTime,LocalDate等接收来自前端请求json的时间字段时,会由于格式问题会导致反序列化失败,这里提供一种全局的解决方案。

1. 示例

1.1 代码

build.gradle

plugins {
  id 'org.springframework.boot' version '2.1.11.RELEASE'
  id 'io.spring.dependency-management' version '1.0.8.RELEASE'
  id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
  mavenCentral()
  maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation "org.apache.commons:commons-lang3:3.9}"
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

java代码

@Date
public class ComputeParam {
    LocalDateTime createTime;
}

 @PostMapping("/compute")
public Stirng compute(@RequestBody ComputeParam param) {
    return ok;
}

1.2 请求

curl --request POST \
  --url http://localhost:8080/compute \
  --header 'content-type: application/json' \
  --data '{"createTime": "2020-01-04 12:12:12"}'

1.3 响应

异常信息

2020-01-05 00:41:33.506  WARN [,fa2f61967e660396,fa2f61967e660396,false] 84616 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2020-01-04 12:12:12": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2020-01-04 12:12:12' could not be parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "2020-01-04 12:12:12": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2020-01-04 12:12:12' could not be parsed at index 10
 at [Source: (PushbackInputStream); line: 2, column: 16] (through reference chain: cn.justme.justmerule.bean.param.ComputeParam["createTime"])]

解决方案

注册自定义LocalDateTimeDeserializer

代码

// 全局生效
@JsonComponent
public class JsonConfig {
    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            if (StringUtils.isNumeric(jsonParser.getValueAsString())) { // 如果是时间戳形式
                return new Date(jsonParser.getValueAsLong()).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime()
            } else { // 默认是yyyy-MM-dd HH:mm:ss 形式,如果有多种格式,可以自行拓展
                return LocalDateTime.parse(jsonParser.getValueAsString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
            }
        }
    }
}

关于@JsonComponent参照官网

如果不想全局生效:

  1. 我们可以去掉@JsonComponent注解,然后在ComputeParam实体类上添加@JsonDeserialize,如下:

    @Date
    public class ComputeParam {
        @JsonDeserialize(using = JsonConfig.LocalDateTimeDeserializer.class)
        LocalDateTime createTime;
    }

    这样可以指定特定的Deserializer,而不影响全局其他同类型的字段。

  2. 更简单的直接在createTime字段上增加 @JsonFormat注解即可

    @Date
    public class ComputeParam {
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        LocalDateTime createTime;
    }

文章作者: shiv
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 shiv !
评论
  目录