2017-09-17 137 views
3

我有具有timestamp屬性的模型:傑克遜:解析自定義偏移日期時間

class Model { 
    @JsonProperty("timestamp") 
    private OffsetDateTime timestamp; 
} 

時間戳的格式如下:

2017-09-17 13:45:42.710576+02 

OffsetDateTime無法解析此:

com.fasterxml.jackson.databind.exc.InvalidFormatException:無法反序列化java.time.OffsetDateTime類型的值從字符串「2017-09-17 13:45:42.710576 + 02」:文本'2017-09-17 13:45:42.710576 + 02'無法在索引10處解析

我該如何解決此問題?

回答

2

你必須告訴傑克遜日期是什麼格式。基本上,你有year-month-day其次是hour:minute:second.microseconds和2位數的偏移量(+02)。所以你的模式將是:

@JsonProperty("timestamp") 
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSSSSx") 
private OffsetDateTime timestamp; 

看看all the date/time patterns的更詳細的解釋。


如果你想保留相同的OffsetDateTime偏移(+02),不要忘記調整DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE optionfalse

如果此選項設置爲true(在我的測試),結果被轉換爲UTC(但它實際上轉換到任何時區在傑克遜配置):

2017-09-17T11:45 :42.710576Z

如果我設置爲false,偏移量在輸入中使用被保留:

2017-09-17T13:45:42.710576 + 02:00


上述代碼用小數點後恰好6位數字的工作原理。但是,如果此數量變化,您可以使用可選模式,由[]分隔。

例如:如果輸入可以有6位或3位十進制數字,我可以使用pattern = "yyyy-MM-dd HH:mm:ss.[SSSSSS][SSS]x"。可選部分[SSSSSS][SSS]告訴解析器考慮6位或3位數字。

可選模式的問題在於,在序列化時,它會打印所有模式(因此它將打印兩秒的小數部分:帶有3位數的6 )。


另一種方法是(通過延伸com.fasterxml.jackson.databind.JsonSerializercom.fasterxml.jackson.databind.JsonDeserializer)來創建定製序列器和解串器:

public class CustomDeserializer extends JsonDeserializer<OffsetDateTime> { 

    private DateTimeFormatter formatter; 

    public CustomDeserializer(DateTimeFormatter formatter) { 
     this.formatter = formatter; 
    } 

    @Override 
    public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException { 
     return OffsetDateTime.parse(parser.getText(), this.formatter); 
    } 
} 

public class CustomSerializer extends JsonSerializer<OffsetDateTime> { 

    private DateTimeFormatter formatter; 

    public CustomSerializer(DateTimeFormatter formatter) { 
     this.formatter = formatter; 
    } 

    @Override 
    public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException { 
     gen.writeString(value.format(this.formatter)); 
    } 
} 

然後,可以註冊那些在JavaTimeModule。如何配置這將取決於您使用的環境(例如:在Spring中,您可以在xml files中進行配置)。我將以編程方式作爲示例。

首先創建格式化器,使用java.time.format.DateTimeFormatterBuilder

DateTimeFormatter formatter = new DateTimeFormatterBuilder() 
    // date/time 
    .appendPattern("yyyy-MM-dd HH:mm:ss") 
    // optional fraction of seconds (from 0 to 9 digits) 
    .optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).optionalEnd() 
    // offset 
    .appendPattern("x") 
    // create formatter 
    .toFormatter(); 

此格式接受的第二與0到9數字的可選部分。然後,我用的是自定義類以上和ObjectMapper註冊它們:

// set formatter in the module and register in object mapper 
ObjectMapper mapper = new ObjectMapper(); 
mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false); 
JavaTimeModule module = new JavaTimeModule(); 
module.addSerializer(OffsetDateTime.class, new CustomSerializer(formatter)); 
module.addDeserializer(OffsetDateTime.class, new CustomDeserializer(formatter)); 
mapper.registerModule(module); 

我還從現場移除@JsonFormat註釋:

@JsonProperty("timestamp") 
private OffsetDateTime timestamp; 

而現在它接受像2017-09-17 13:45:42+02(秒無分數值)和2017-09-17 13:45:42.71014+02(5個十進制數字)。它可以解析0到9位十進制數字(9是API支持的最大值),並且在序列化時打印完全相同的數量。


上面的替代方法非常靈活,因爲它允許在自定義類中設置格式器。但它也爲所有OffsetDateTime字段設置序列化和反序列化。

如果你不希望出現這種情況,你也可以創建一個類有固定格式:

static class CustomDeserializer extends JsonDeserializer<OffsetDateTime> { 

    private DateTimeFormatter formatter = // create formatter as above 

    // deserialize method is the same 
} 

static class CustomSerializer extends JsonSerializer<OffsetDateTime> { 

    private DateTimeFormatter formatter = // create formatter as above 

    // serialize method is the same 
} 

然後,您可以添加那些只想要的字段,使用註釋com.fasterxml.jackson.databind.annotation.JsonSerializecom.fasterxml.jackson.databind.annotation.JsonDeserialize

@JsonProperty("timestamp") 
@JsonSerialize(using = CustomSerializer.class) 
@JsonDeserialize(using = CustomDeserializer.class) 
private OffsetDateTime timestamp; 

有了這個,你不需要在模塊中註冊自定義序列化,只有註解的字段將使用自定義類(其他OffsetDateTime字段將使用默認設置)。

+0

有趣的是,我不知道你可以像這樣定義日期格式。我應該使用'ZonedDateTime'的'OffsetDateTime'嗎? –

+0

@KwersT如果你只有偏移+02,我建議OffsetDateTime。如果存在時區名稱(例如歐洲/倫敦)和夏令時規則,請使用ZonedDateTime –

+2

謝謝,這很好。 –