2015-08-15 62 views
1

我在Spring Boot應用程序中使用Java 8 DateTime和Jackson jsr 310支持。Java 8 DateTime序列化和傑克遜JSR 310

我禁用了SerializationFeature.WRITE_DATES_AS_TIMESTAMPS強制Jackson將localDatetime序列化爲string而不是int數組。

但是我發現一個奇怪的格式問題,當日期時間微秒或nonaseconds是0。最後,我認爲序列化的結果可能是等於date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),但它不是,格式方法省略了零。

完整的示例測試代碼。

private LocalDateTime date; 

private OffsetDateTime offsetDate; 

private ZonedDateTime zonedDate; 

@Before 
public void setup() throws ServletException { 
    date = LocalDateTime.of(2015, 8, 15, 11, 40, 10, 100_000_000); 
    offsetDate = OffsetDateTime.of(2015, 8, 15, 11, 40, 10, 100_000_000, ZoneOffset.ofHours(8)); 
    zonedDate = ZonedDateTime.of(2015, 8, 15, 11, 40, 10, 100_000_000, ZoneId.of("Asia/Shanghai")); 
} 

@Test 
public void testDateFormat() throws Exception { 
    Map<String, Object> map = new HashMap<>(); 
    map.put("localDate", date); 
    map.put("offsetDate", offsetDate); 
    map.put("zonedDate", zonedDate); 

    String json = objectMapper.writeValueAsString(map); 

    log.debug("converted json result @" + json); 

    JsonNode rootNode = objectMapper.readTree(json); 

    JsonNode localDateNode = rootNode.get("localDate"); 
    assertEquals("local date should be equals", date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), localDateNode.textValue()); 

    JsonNode offsetDateNode = rootNode.get("offsetDate"); 
    assertEquals("offsetDate date should be equals", offsetDate.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), offsetDateNode.textValue()); 

    JsonNode zonedDateNode = rootNode.get("zonedDate"); 
    assertEquals("zonedDate date should be equals", zonedDate.format(DateTimeFormatter.ISO_ZONED_DATE_TIME), zonedDateNode.textValue()); 

} 

測試失敗,和日誌調試輸出

{ 
    "zonedDate":"2015-08-15T11:40:10.100+08:00[Asia/Shanghai]", 
    "localDate":"2015-08-15T11:40:10.100", 
    "offsetDate":"2015-08-15T11:40:10.1+08:00" 
} 

zonedDateLOCALDATE的微秒的一端與,但offsetDate不是。並且date.format也得到不同的結果,其中千分之一是不是以結尾。

格式,的toString中,JSON文本

LocalDateTime format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)@2015-08-15T11:40:10.1 
LocalDateTime toString          @2015-08-15T11:40:10.100 
LocalDateTime serialized json node text     @2015-08-15T11:40:10.100 
OffsetDateTime format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)@2015-08-15T11:40:10.1+08:00 
OffsetDateTime toString          @2015-08-15T11:40:10.100+08:00 
OffsetDateTime serialized json node text      @2015-08-15T11:40:10.1+08:00 
ZonedDateTime format(DateTimeFormatter.ISO_ZONED_DATE_TIME)@2015-08-15T11:40:10.1+08:00[Asia/Shanghai] 
ZonedDateTime toString          @2015-08-15T11:40:10.100+08:00[Asia/Shanghai] 
ZonedDateTime serialized json node text     @2015-08-15T11:40:10.100+08:00[Asia/Shanghai] 
  1. 看來的toString結果應該是在Jackson序列所期望的結果,在上面記錄的打印結果,OffsetDateTime JSON節點微秒文本應以結束。
  2. 爲什麼format方法的結果省略了結尾?

完整的示例代碼可以從我的github.com找到。

angular-springmvc-sample-boot

ISODateTest

回答

0

您的測試有2個問題:

  1. 你比較蘋果和橘子,ObjectMapper序列化結果和DateTimeFormatter格式的結果。每個人都有自己的默認和配置選項,結果不一樣也就不足爲奇了。
  2. 100_000_000確實是1並根據ISO_LOCAL_TIME的定義,DateTimeFormatter打印出「一秒到九位數的納秒級,按需輸出多少位數」。在你的情況下,只需要1位數字,這正是你在LocalDateTime的輸出中得到的。

我不完全清楚你要在這裏做什麼,以及你寫的測試的目的是什麼。如果您預期格式化程序的精度爲3位數,則必須自行打印。根據您的代碼中的toString格式設置是個不錯的主意。

+0

感謝您的解釋。我感到困惑的是爲什麼輸出格式沒有在Jackson _ObjectMapper_中統一。 – Hantsy

+0

如果通過「團結」,你的意思是如果ObjectMapper內部使用DateTimeFormatter,它就是。請參閱[JavaTimeModule.java](https://github.com/FasterXML/jackson-datatype-jsr310/blob/master/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule.java)。但是在Jackson中有很多配置可能會影響datetime被解析(或格式化)的方式。有關詳細信息,請參閱'com.fasterxml.jackson.databind.SerializationFeature'和'com.fasterxml.jackson.databind.DeserializationFeature'。 –

+0

看來最新的2.7.2在LocalDateTime和OffsetDateTime序列化中使用了'DateTimeFormatter'。但是'ZonedDateTime'序列化不使用'DateTimeFormatter.ISO_ZONED_DATE_TIME'。 – Hantsy

3

作爲一種變通方法,添加我自己的序列:

ObjectMapper objectMapper = new ObjectMapper(); 
    objectMapper.registerModule(new JavaTimeModule()); 
    SimpleModule module = new SimpleModule(); 
    module.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() { 
     @Override 
     public void serialize(ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { 
      jsonGenerator.writeString(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ").format(zonedDateTime)); 
     } 
    }); 
    objectMapper.registerModule(module); 
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT); 
+0

代碼片段很有幫助。謝謝。 – Hantsy

0

這裏是我做了配置ObjectMapper。

@Configuration 
public class JacksonConfiguration { 

    @Bean 
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { 
    ObjectMapper objectMapper = builder.build(); 
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 
    objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false); 
    return objectMapper; 
    } 
}