2012-09-17 55 views
82

我有一個日期格式的API來是這樣的:日期格式映射到JSON傑克遜

"start_time": "2015-10-1 3:00 PM GMT+1:00" 

也就是YYYY-MM-DD HH:MM AM/PM GMT時間戳。 我將這個值映射到POJO中的Date變量。顯然,它顯示轉換錯誤。

我想知道兩件事情:

  1. 是什麼格式,我需要用它來進行轉換與傑克遜?日期是一個很好的字段類型嗎?
  2. 一般來說,有沒有辦法在變量被Jackson映射到Object成員之前處理變量?喜歡的東西,改變格式,計算等

回答

70

是什麼格式,我需要用它來進行轉換與傑克遜?日期是一個很好的字段類型嗎?

Date是一個很好的字段類型。您可以通過使用ObjectMapper.setDateFormat使JSON解析,能夠很容易地:

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z"); 
myObjectMapper.setDateFormat(df); 

一般情況下,有沒有辦法來處理變量,他們被映射由傑克遜對象成員之前?類似的,改變格式,計算等。

是的。您有幾個選項,包括實施自定義JsonDeserializer,例如延伸JsonDeserializer<Date>This是一個好的開始。

+2

如果格式還包含AM/PM指定,12小時格式會更好:DateFormat df = new SimpleDateFormat(「yyyy-MM-dd hh:mm a z」); –

+0

嘗試了所有這些解決方案,但無法將我的POJO的Date變量存儲到Map鍵值中,也作爲Date存儲。 我想要這個從Map實例化一個BasicDbObject(MongoDB API),然後將這個變量作爲Date(不是Long或String)存儲在MongoDB DB集合中。 這甚至可能嗎? 謝謝 – RedEagle

+1

使用Java 8'LocalDateTime'或'ZonedDateTime'代替'Date'是否容易?由於'日期'基本上已被棄用(或至少它的許多方法),我想使用這些替代品。 – houcros

3

這真是很好的例子,在類的田間地頭註釋: http://java.dzone.com/articles/how-serialize-javautildate

+0

如果您嘗試在您的應用程序中爲java.util.Date序列化設置全局配置,則此解決方案不起作用,也會污染您的域模型。 – realPK

+0

@PK它不是一個全局配置,你必須添加註釋到你的域模型的日期字段。 – digz6666

1

現在他們已經對日期處理維基頁面: http://wiki.fasterxml.com/JacksonFAQDateHandling

+0

很酷。謝謝,@Sutra。 –

+0

這非常有用。通過使用objMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false)更改對象映射器,可以在應用程序中全局設置它。 //將日期寫成一個字符串,例如2015-07-06T20:38:41.960 + 0000 –

+1

鏈接斷掉。截至目前,整個網站都拒絕連接。 – cbmeeks

205

由於傑克遜V2.0,你可以使用對象成員直接使用@JsonFormat註解;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z") 
private Date date; 
+38

如果你想包括時區: '@JsonFormat(shape = JsonFormat.Shape.STRING ,pattern =「yyyy-MM-dd'T'HH:mm:ss.SSS'Z'」,timezone =「GMT」)' – realPK

+0

嗨,哪個jar有這個註釋。我正在使用jackson-mapper-asl 1.9.10版本。我沒有得到它 –

+1

@Ramki:jackson-annotations> = 2.0 –

37

當然中有自動的方式稱爲序列和反序列化和可以用作爲通過pb2q所提及的具體說明(@JsonSerialize@JsonDeserialize)定義它。

您可以同時使用java.util.Date和java.util.Calendar 以及可能的JodaTime。反序列化期間

的@JsonFormat註釋不工作對我來說,因爲我想(已調整時區不同的值)(序列化的工作完美):

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "CET") 

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Europe/Budapest") 

您需要如果您需要預測結果,則使用自定義序列化器和自定義反序列化器來代替@JsonFormat註釋。我已經找到真正的好教程和解決方案在這裏http://www.baeldung.com/jackson-serialize-dates

有用於日期領域的例子,但我需要日曆字段,以便這裏是我實現

串行類:

public class CustomCalendarSerializer extends JsonSerializer<Calendar> { 

    public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 
    public static final Locale LOCALE_HUNGARIAN = new Locale("hu", "HU"); 
    public static final TimeZone LOCAL_TIME_ZONE = TimeZone.getTimeZone("Europe/Budapest"); 

    @Override 
    public void serialize(Calendar value, JsonGenerator gen, SerializerProvider arg2) 
      throws IOException, JsonProcessingException { 
     if (value == null) { 
      gen.writeNull(); 
     } else { 
      gen.writeString(FORMATTER.format(value.getTime())); 
     } 
    } 
} 

The 解串器 c小姑娘:

public class CustomCalendarDeserializer extends JsonDeserializer<Calendar> { 

    @Override 
    public Calendar deserialize(JsonParser jsonparser, DeserializationContext context) 
      throws IOException, JsonProcessingException { 
     String dateAsString = jsonparser.getText(); 
     try { 
      Date date = CustomCalendarSerializer.FORMATTER.parse(dateAsString); 
      Calendar calendar = Calendar.getInstance(
       CustomCalendarSerializer.LOCAL_TIME_ZONE, 
       CustomCalendarSerializer.LOCALE_HUNGARIAN 
      ); 
      calendar.setTime(date); 
      return calendar; 
     } catch (ParseException e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 

和使用上述種類的

public class CalendarEntry { 

    @JsonSerialize(using = CustomCalendarSerializer.class) 
    @JsonDeserialize(using = CustomCalendarDeserializer.class) 
    private Calendar calendar; 

    // ... additional things ... 
} 

使用本實施序列化和反序列化處理的執行導致連續的原點的值。

只有使用@JsonFormat標註反序列化提供了不同的結果我想是因爲圖書館內部時區缺省的設置你不能標註參數的變化(這是我與傑克遜庫2.5.3和2.6體驗。 3版本)。

+2

昨天我已經downvote爲我的答案。我在這個話題上做了很多工作,所以我不明白。我可以從中獲得一些反饋意見嗎?如果遇到downvote,我將不勝感激。這樣我們可以彼此瞭解更多。 –

+0

很好的答案,謝謝它真的幫助我!小建議 - 考慮將CustomCalendarSerializer和CustomCalendarDeserializer作爲靜態類放在封閉的父類中。我認爲這會讓代碼變得更好:) – Stuart

+0

@Stuart - 你應該簡單地將代碼的重組作爲另一個答案,或者提出一個編輯。米克洛斯可能沒有時間自由去做。 – ocodo

1

大廈@ miklov-kriven的非常有用的答案,我希望考慮這兩個附加分證明是有益的人:

(1)我覺得這是一個不錯的主意,包括串行器和解串器的靜態內部班級在同一班。注意,使用ThreadLocal來實現SimpleDateFormat的線程安全。

public class DateConverter { 

    private static final ThreadLocal<SimpleDateFormat> sdf = 
     ThreadLocal.<SimpleDateFormat>withInitial(
       () -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm a z");}); 

    public static class Serialize extends JsonSerializer<Date> { 
     @Override 
     public void serialize(Date value, JsonGenerator jgen SerializerProvider provider) throws Exception { 
      if (value == null) { 
       jgen.writeNull(); 
      } 
      else { 
       jgen.writeString(sdf.get().format(value)); 
      } 
     } 
    } 

    public static class Deserialize extends JsonDeserializer<Date> { 
     @Overrride 
     public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws Exception { 
      String dateAsString = jp.getText(); 
      try { 
       if (Strings.isNullOrEmpty(dateAsString)) { 
        return null; 
       } 
       else { 
        return new Date(sdf.get().parse(dateAsString).getTime()); 
       } 
      } 
      catch (ParseException pe) { 
       throw new RuntimeException(pe); 
      } 
     } 
    } 
} 

(2)作爲替代使用每個單獨的類成員,你也可以考慮通過在應用級應用自定義序列覆蓋傑克遜的默認序列化的@JsonSerialize和@JsonDeserialize註解,這是所有類成員類型Date將由Jackson使用此自定義序列化進行序列化,而不在每個字段上進行明確的註釋。如果您在使用Spring啓動例如一個辦法做到這一點將按如下:

@SpringBootApplication 
public class Application { 
    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 

    @Bean 
    public Module customModule() { 
     SimpleModule module = new SimpleModule(); 
     module.addSerializer(Date.class, new DateConverter.Serialize()); 
     module.addDeserializer(Date.class, new Dateconverter.Deserialize()); 
     return module; 
    } 
} 
+0

我低估了你(我討厭downvoting,但我不想讓人們使用你的答案)。 SimpleDateFormat不是線程安全的。這是2016年(你在2016年回答)。有許多更快和線程安全選項時,您不應該使用SimpleDateFormat。甚至有一個確切的誤用你在這裏提出的Q/A:http://stackoverflow.com/questions/25680728/json-serializer-and-thread-safety –

+1

@AdamGent感謝您指出這一點。在這種情況下,使用Jackson,ObjectMapper類是線程安全的,因此無關緊要。但是,我確實認爲代碼可能被複制並用於非線程安全的上下文中。所以我編輯了我的答案,以使SimpleDateFormat線程安全。我也承認有替代品,主要是java.time包。 – Stuart

1

如果任何人有使用自定義的日期格式爲java.sql中的問題。迄今爲止,這是最簡單的解決方案:

ObjectMapper mapper = new ObjectMapper(); 
SimpleModule module = new SimpleModule(); 
module.addSerializer(java.sql.Date.class, new DateSerializer()); 
mapper.registerModule(module); 

(這個所謂的答案救了我很多麻煩:https://stackoverflow.com/a/35212795/3149048

傑克遜使用SqlDateSerializer默認爲java.sql.Date,但目前,這序列化程序不考慮dateformat,請參閱此問題:https://github.com/FasterXML/jackson-databind/issues/1407。 解決方法是爲java.sql.Date註冊不同的序列化程序,如代碼示例中所示。