【问题标题】:Deserialize Java 8 LocalDateTime with JacksonMapper使用 JacksonMapper 反序列化 Java 8 LocalDateTime
【发布时间】:2017-03-12 16:44:15
【问题描述】:

我已经在 SO 中阅读了几个关于java.time.LocalDateTime 和 JSON 属性之间的序列化和反序列化的问题,但我似乎无法让它工作。

我已设法将我的 Spring Boot 应用程序配置为以我想要的格式 (YYY-MM-dd HH:mm) 返回日期,但我在接受这种 JSON 格式的值时遇到问题。

这些是我到目前为止所做的所有事情:

jsr310添加了maven依赖:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

在我的主课中指定jsr310

@EntityScan(basePackageClasses = { App.class, Jsr310JpaConverters.class })

application.properties 中禁用序列化作为时间戳:

spring.jackson.serialization.write_dates_as_timestamps=false

这是我的日期时间实体映射:

@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;

在我的数据库中,我将此日期存储为 TIMESTAMP,格式如下:2016-12-01T23:00:00+00:00

如果我通过控制器访问该实体,它会返回具有正确 startDate 格式的 JSON。但是,当我尝试使用YYYY-MM-dd HH:mm 格式发布并反序列化它时,出现以下异常:

{
  "timestamp": "2016-10-30T14:22:25.285+0000",
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.http.converter.HttpMessageNotReadableException",
  "message": "Could not read document: Can not deserialize value of type java.time.LocalDateTime from String \"2017-01-01 20:00\": Text '2017-01-01 20:00' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MonthOfYear=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2017, DayOfMonth=1},ISO resolved to 20:00 of type java.time.format.Parsed\n at [Source: java.io.PushbackInputStream@679a734d; line: 6, column: 16] (through reference chain: com.gigsterous.api.model.Event[\"startDate\"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.time.LocalDateTime from String \"2017-01-01 20:00\": Text '2017-01-01 20:00' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MonthOfYear=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2017, DayOfMonth=1},ISO resolved to 20:00 of type java.time.format.Parsed\n at [Source: java.io.PushbackInputStream@679a734d; line: 6, column: 16] (through reference chain: com.gigsterous.api.model.Event[\"startDate\"])",
  "path": "/api/events"
}

我知道关于这个主题有很多答案,但是关注他们并尝试了几个小时并没有帮助我弄清楚我做错了什么,所以如果有人能向我指出我错过了什么,我会很高兴.感谢您对此的任何意见!

编辑:这些是该过程中涉及的所有类:

存储库:

@Repository
public interface EventRepository extends PagingAndSortingRepository<Event, Long> {
}

控制器:

@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Event> createEvent(@RequestBody Event event) {
        return new ResponseEntity<>(eventRepo.save(event), HttpStatus.CREATED);
}

我的 JSON 请求payalod:

{
  "name": "Test",
  "startDate": "2017-01-01 20:00"
}

事件:

@Entity
@Table(name = "events")
@Getter
@Setter
public class Event {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "event_id")
    private long id;

    @Column(name = "name")
    private String name;

    @Column(name = "start_date")
    @DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
    @JsonFormat(pattern = "YYYY-MM-dd HH:mm")
    private LocalDateTime startDate;
}

【问题讨论】:

  • 能否请您分享其余的跟踪信息,以便我们都可以验证哪些类参与解析该值? DateTimeFormat 注释也显得格格不入
  • @Ivan 我将所有详细信息添加到我的问题中并更新了跟踪
  • 你做了什么特别的事情让它输出这样的错误信息吗?我有一个类似的问题,除了我的 localdatetime 在发布请求中只是作为 null 出现,即使我可以执行 Get 并且它的格式很好。

标签: java json jpa spring-boot jackson


【解决方案1】:

您传递的日期时间不是 ISO 本地日期时间格式。

改成

@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME)
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;

并以“2011-12-03T10:15:30”格式传递日期字符串。

但如果你仍然想传递你的自定义格式,你只需要指定正确的格式化程序。

改成

@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;

我认为您的问题是 @DateTimeFormat 根本没有效果。由于 Jackson 正在进行反序列化,它对 spring 注释一无所知,我没有看到 spring 在反序列化上下文中扫描此注释。

或者,您可以尝试在注册 Java 时间模块时设置格式化程序。

LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);

这是带有解串器的测试用例,它工作正常。也许尝试完全摆脱那个 DateTimeFormat 注释。

@RunWith(JUnit4.class)
public class JacksonLocalDateTimeTest {

    private ObjectMapper objectMapper;

    @Before
    public void init() {
        JavaTimeModule module = new JavaTimeModule();
        LocalDateTimeDeserializer localDateTimeDeserializer =  new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
        module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
        objectMapper = Jackson2ObjectMapperBuilder.json()
                .modules(module)
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .build();
    }

    @Test
    public void test() throws IOException {
        final String json = "{ \"date\": \"2016-11-08 12:00\" }";
        final JsonType instance = objectMapper.readValue(json, JsonType.class);

        assertEquals(LocalDateTime.parse("2016-11-08 12:00",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") ), instance.getDate());
    }
}


class JsonType {
    private LocalDateTime date;

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }
}

【讨论】:

  • 我有一个错误,说我无法从 DateTimeFormatter 转换为 DateTimeFormat.ISO
  • 使用该格式化程序的弹簧等价物。你的想法对吗?
  • 我现在无法测试它。你可以试试@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
  • DateTimeFormatter.ISO_LOCAL_DATE 抛出编译时错误:attribute value must be constant
  • 正如@ankit 所说,这不会编译。 Attribute value must be constant
【解决方案2】:

你使用了错误的字母大小写:

@JsonFormat(pattern = "YYYY-MM-dd HH:mm")

应该是:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm")

有了这个改变,一切都按预期工作。

【讨论】:

    【解决方案3】:

    这对我有用:

     @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", shape = JsonFormat.Shape.STRING)
     private LocalDateTime startDate;
    

    【讨论】:

    • 是的.. 正在寻找这个东西。对于问题“2016-10-30T14:22:25.285+0000”中提到的确切日期时间值,这是对我有用的解决方案。
    【解决方案4】:

    更新:

    改为:

    @Column(name = "start_date")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm", iso = ISO.DATE_TIME)
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
    private LocalDateTime startDate;
    

    JSON 请求:

    {
     "startDate":"2019-04-02 11:45"
    }
    

    【讨论】:

      【解决方案5】:

      你可以实现你的 JsonSerializer

      见:

      你在 bean 中的属性

      @JsonProperty("start_date")
      @JsonFormat("YYYY-MM-dd HH:mm")
      @JsonSerialize(using = DateSerializer.class)
      private Date startDate;
      

      这样实现你的自定义类

      public class DateSerializer extends JsonSerializer<Date> implements ContextualSerializer<Date> {
      
          private final String format;
      
          private DateSerializer(final String format) {
              this.format = format;
          }
      
          public DateSerializer() {
              this.format = null;
          }
      
          @Override
          public void serialize(final Date value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException {
              jgen.writeString(new SimpleDateFormat(format).format(value));
          }
      
          @Override
          public JsonSerializer<Date> createContextual(final SerializationConfig serializationConfig, final BeanProperty beanProperty) throws JsonMappingException {
              final AnnotatedElement annotated = beanProperty.getMember().getAnnotated();
              return new DateSerializer(annotated.getAnnotation(JsonFormat.class).value());
          }
      
      }
      

      在我们发布结果后试试这个。

      【讨论】:

      【解决方案6】:

      这对我有用:

      import org.springframework.format.annotation.DateTimeFormat;
      import org.springframework.format.annotation.DateTimeFormat.ISO;
      
      @Column(name="end_date", nullable = false)
      @DateTimeFormat(iso = ISO.DATE_TIME)
      @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
      private LocalDateTime endDate;
      

      【讨论】:

        【解决方案7】:

        您的代码有两个问题:

        1。使用错误的类型

        LocalDateTime 不支持时区。下面给出的是overview of java.time types,您可以看到与您的日期时间字符串匹配的类型2016-12-01T23:00:00+00:00OffsetDateTime,因为它的区域偏移量为+00:00

        如下更改您的声明:

        private OffsetDateTime startDate;
        

        2。使用错误的格式

        格式有两个问题:

        • 您需要使用 y (year-of-era ) 而不是 Y (week-based-year)。检查this discussion 了解更多信息。事实上,我建议你使用u(年份)而不是y(年份)。更多详情请查看this answer
        • 偏移部分需要使用XXXZZZZZ,即格式应为uuuu-MM-dd'T'HH:m:ssXXX

        查看DateTimeFormatterdocumentation page,了解有关这些符号/格式的更多详细信息。

        演示:

        import java.time.OffsetDateTime;
        import java.time.format.DateTimeFormatter;
        
        public class Main {
            public static void main(String[] args) {
                String strDateTime = "2019-10-21T13:00:00+02:00";
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:m:ssXXX");
                OffsetDateTime odt = OffsetDateTime.parse(strDateTime, dtf);
                System.out.println(odt);
            }
        }
        

        输出:

        2019-10-21T13:00+02:00
        

        Trail: Date Time 了解有关现代日期时间 API 的更多信息。

        【讨论】:

          【解决方案8】:

          它对我有用..

          @Column(name = "lastUpdateTime")
          @DateTimeFormat(iso = ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss" )   
          @JsonIgnore
          

          【讨论】:

            猜你喜欢
            • 2023-04-04
            • 1970-01-01
            • 1970-01-01
            • 2019-11-27
            • 1970-01-01
            • 1970-01-01
            • 2016-09-07
            • 1970-01-01
            • 2021-01-19
            相关资源
            最近更新 更多