使用JacksonMapper反序列化Java 8 LocalDateTime

Sma*_*ajl 27 java json jpa jackson spring-boot

我在SO中有关于序列化和反序列化java.time.LocalDateTime以及JSON属性的解答已经阅读了几个问题,但我似乎无法使其正常工作.

我已经设法配置我的Spring Boot应用程序以我希望的格式返回日期(YYY-MM-dd HH:mm)但是我在接受JSON中的这种格式的值时遇到了问题.

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

添加了maven依赖关系jsr310:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

jsr310在我的主要课程中指定:

@EntityScan(basePackageClasses = { App.class, Jsr310JpaConverters.class })
Run Code Online (Sandbox Code Playgroud)

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

spring.jackson.serialization.write_dates_as_timestamps=false
Run Code Online (Sandbox Code Playgroud)

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

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

在我的数据库中,我将此日期存储为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"
}
Run Code Online (Sandbox Code Playgroud)

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

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

库:

@Repository
public interface EventRepository extends PagingAndSortingRepository<Event, Long> {
}
Run Code Online (Sandbox Code Playgroud)

控制器:

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

我的JSON请求payalod:

{
  "name": "Test",
  "startDate": "2017-01-01 20:00"
}
Run Code Online (Sandbox Code Playgroud)

事件:

@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;
}
Run Code Online (Sandbox Code Playgroud)

use*_*814 33

您传递的日期时间不是iso本地日期时间格式.

改成

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

并以"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;
Run Code Online (Sandbox Code Playgroud)

我认为你的问题是@DateTimeFormat根本没有效果.由于杰克逊正在进行去分离,并且它对弹簧注释一无所知,我也没有看到Spring在反序列化上下文中扫描这个注释.

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

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

以下是使用deseralizer的测试用例.可能会尝试完全摆脱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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • DateTimeFormatter.ISO_LOCAL_DATE抛出编译时错误:`属性值必须是常量` (6认同)
  • 我有一个错误,说我无法从DateTimeFormatter转换为DateTimeFormat.ISO (2认同)
  • 我现在不能测试它.你能试试@DateTimeFormat(pattern ="yyyy-MM-dd HH:mm") (2认同)
  • 正如@ankit所说,这不会编译。属性值必须是常数 (2认同)

Mac*_*iak 17

你在一年中使用了错误的字母案例:

@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
Run Code Online (Sandbox Code Playgroud)

应该:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
Run Code Online (Sandbox Code Playgroud)

通过此更改,一切都按预期工作.


Arv*_*ash 10

您的代码有两个问题:

1.使用错误的类型

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

在此输入图像描述

更改您的声明如下:

private OffsetDateTime startDate;
Run Code Online (Sandbox Code Playgroud)

2. 使用错误的格式

格式上有两个问题:

  • 您需要使用y(year-of-era ) 而不是Y(week-based-year)。查看此讨论以了解更多信息。事实上,我建议您使用u(year) 而不是y(year-of-era )。检查此答案以获取更多详细信息。
  • 您需要使用XXXorZZZZZ作为偏移部分,即您的格式应该是uuuu-MM-dd'T'HH:m:ssXXX

有关这些符号/格式的更多详细信息,请查看的文档页面。DateTimeFormatter

演示:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

2019-10-21T13:00+02:00
Run Code Online (Sandbox Code Playgroud)

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


小智 9

这对我有用:

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


小智 7

更新:

改成:

@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;
Run Code Online (Sandbox Code Playgroud)

JSON 请求:

{
 "startDate":"2019-04-02 11:45"
}
Run Code Online (Sandbox Code Playgroud)