2015-03-30 48 views
11

我試圖堅持java對象java.util.Date字段在使用fasterxml傑克遜mongo集合。 問題是objectMapper的默認性質是將Date存儲爲NumberLong類型。如何將日期字段作爲ISODate()在MongoDb中使用傑克遜

對於e.g,java.util.Date類型的createdTime場被存儲如下:

"createdTime" : NumberLong("1427728445176")

我想將其存儲在ISODate格式是蒙戈殼牌可用。

現在,我知道有方法來格式化對象映射器以將日期存儲在字符串dateformat中。 但我只是在尋找ISODate()格式。

對於e.g "createdTime" : ISODate("2015-01-20T16:39:42.132Z")

有沒有辦法做到這一點? 請指教大師。 在此先感謝您的幫助。

回答

9

你需要的是Jackson Joda Module。如果導入到你的類路徑中,你可以做以下你的映射器把它寫爲您的期望時間戳:

ObjectMapper mapper = new ObjectMapper(); 
mapper.registerModule(new JodaModule()); 
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true); 
mapper.writeValueAsString(date); 

您可以在代碼示例上面的POJO,必要時更換date

編輯: 它看起來像你真正想要的是一個自定義序列化程序。這將是這個樣子:

public class IsoDateSerializer extends JsonSerializer<DateTime> { 
    @Override 
    public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider provider) { 
     String isoDate = ISODateTimeFormat.dateTime().print(value); 
     jgen.writeRaw("ISODATE(\"" + isoDate + "\")"); 
    } 

然後你要麼註冊它的映射爲所有時間類型

mapper.addSerializer(DateTime.class, new IsoDateSerializer()); 

或使用註釋

@JsonSerializer(using = IsoDateSerializer.class) 
public DateTime createdTime; 
+1

謝謝@xathien。我早就已經嘗試過這種方法,但採用這種方法,它本質上創建了一個字符串。 例如,在改變我的代碼映射器後,如下所示 ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JodaModule()); mapper.configure(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false); \t \t \t \t 輸出是如下 「createdTime」: 「2015-03-30T19:33:08.921Z」。 我正在尋找類似下面的格式。 「createdTime」:ISODate(「2015-01-20T16:39:42.132Z」)\t \t 或者我錯過了什麼? – vishy 2015-03-30 19:38:01

+0

查看更新的答案。 – xathien 2015-03-30 19:54:36

+0

感謝@xathien。我用我的答案更新了這篇文章,但接受你的回覆作爲答案,因爲它指出我正確的方向:) – vishy 2015-04-02 01:02:47

6

指定它的功能我能夠將日期字符串序列化爲ISODate格式。我寫了一個如下所示的客戶日期序列化程序。

public void serialize(Date date, JsonGenerator jgen, SerializerProvider provider) throws IOException { 
    String dateValue = getISODateString(date); 
    String text = "{ \"$date\" : \""+ dateValue +"\"}"; 
    jgen.writeRawValue(text); 
} 

根據用戶@ mmx73的要求,我添加了客戶Date DeSeriaizer的代碼。

public class IsoDateDeSerializer extends JsonDeserializer<Date> { 

    @Override 
    public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) 
      throws IOException, JsonProcessingException { 
     ObjectCodec oc = jsonParser.getCodec(); 
     JsonNode node = oc.readTree(jsonParser); 
     String dateValue = node.get("$date").asText(); 

     //DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 
     DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); 
     Date date = null; 
     try { 
      date = df.parse(dateValue); 
     } catch (ParseException e) { 
      e.printStackTrace(); 
     } 

     return date; 
    } 
} 
+0

而不是硬編碼''s'等等,我建議使用JsonGenerator函數'writeStartObject()','writeFieldName()',然後'writeRawValue()'。否則,很樂意幫忙! – xathien 2015-04-02 02:31:32

+1

很酷的提示!謝謝。 – vishy 2015-04-02 15:15:08

+1

@vishy:感謝上帝,我對此感到生氣。大多數解決方案能夠序列化和反序列化日期,但在MongoDB中存儲爲String或Int64。當添加TTL索引時,兩者都沒有什麼幫助。 2豎起大拇指。 – mmx73 2016-02-07 22:20:45

2

這些答案都沒有完成我想要的。我遇到了麻煩,因爲當我將JSON字符串序列化到MongoDB時,它被存儲爲String。一個很好格式化的字符串,但一個字符串都不會少。

我使用com.fasterxml.jackson.databind.ObjectMapper將我的對象轉換爲/從JSON,我想繼續使用這個類。我有以下方法:

public enum JsonIntent {NONE, MONGODB}; 
public static ObjectMapper getMapper(final JsonIntent intent) { 

    ObjectMapper mapper = new ObjectMapper(); 
    // Setting to true saves the date as NumberLong("1463597707000") 
    // Setting to false saves the data as "2016-05-18T19:30:52.000+0000" 

    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 
    mapper.registerModule(new JodaModule()); 

    if (intent == JsonIntent.MONGODB) { 
     // If you want a date stored in MONGO as a date, then you must store it in a way that MONGO 
     // is able to deal with it. 
     SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null, null, null)); 

     testModule.addSerializer(Date.class, new StdSerializer<Date>(Date.class) { 
      private static final long serialVersionUID = 1L; 

      @Override 
      public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException { 
       try { 
        if (value == null) { 
         jgen.writeNull(); 
        } else { 
         jgen.writeStartObject(); 
         jgen.writeFieldName("$date"); 
         String isoDate = ISODateTimeFormat.dateTime().print(new DateTime(value)); 
         jgen.writeString(isoDate); 
         jgen.writeEndObject(); 
        } 
       } catch (Exception ex) { 
        Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Couldn't format timestamp " + value + ", writing 'null'", ex); 
        jgen.writeNull(); 
       } 
      } 
     }); 

     testModule.addDeserializer(Date.class, new StdDeserializer<Date>(Date.class) { 
      private static final long serialVersionUID = 1L; 

      @Override 
      public Date deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException { 
       JsonNode tree = jp.readValueAsTree(); 
       SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 
       try { 
        return ISODateTimeFormat.dateTime().parseDateTime(tree.get("$date").textValue()).toDate(); 
       } catch (Throwable t) { 
        throw new IOException(t.getMessage(), t); 
       } 
      } 

     }); 

     mapper.registerModule(testModule); 
    } 

    return mapper; 
} 

現在,我可以運行下面的測試代碼:

BObjectMapper mapper = getMapper(JsonUtil.JsonIntent.NONE); 
Date d1 = new Date(); 
String v = mapper.writeValueAsString(d1); 
System.out.println("Joda Mapping: " + v); 
Date d2 = mapper.readValue(v, Date.class); 
System.out.println("Decoded Joda: " + d2); 

mapper = getMapper(JsonUtil.JsonIntent.MONGODB); 
v = mapper.writeValueAsString(d1); 
System.out.println("Mongo Mapping: " + v); 
d2 = mapper.readValue(v, Date.class); 
System.out.println("Decoded Mongo: " + d2); 

輸出如下:

Joda Mapping: "2016-06-13T14:58:11.937+0000" 
Decoded Joda: Mon Jun 13 10:58:11 EDT 2016 
Mongo Mapping: {"$date":"2016-06-13T10:58:11.937-04:00"} 
Decoded Mongo: Mon Jun 13 10:58:11 EDT 2016 

注意,JSON,這將是發送到MONGODB定義的值包含一個名爲「$ date」的字段。這告訴MongoDB這是一個看起來像日期的對象。

當我看着蒙​​戈,我看到以下內容:

"importDate" : ISODate("2016-05-18T18:55:07Z") 

現在,我可以訪問該字段爲日期,而不是作爲一個字符串。

所編碼的JSON字符串添加到蒙戈,我的代碼如下:

MongoDatabase db = getDatabase(); 
Document d = Document.parse(json); 
db.getCollection(bucket).insertOne(d); 

在這種情況下,「JSON」是編碼的JSON字符串。因爲它來自JSON字符串,除非它推斷出這種類型,否則無法知道類型,這就是爲什麼我們需要「$ date」部分。 「桶」只是一個指示要使用哪個表的字符串。

作爲一個提示,我發現如果我從Mongo中取出一個BSON對象,並通過調用doc.toJson()將其轉換爲JSON字符串(其中doc的類型爲org.bison.Document,查詢)時,日期對象以長整型值存儲,而不是格式化文本字符串。我沒有檢查,看看我是否可以用這種方式格式化後的數據推入蒙戈,但是,你可以按如下修改如上圖所示,以支持該解串器:

testModule.addDeserializer(Date.class, new StdDeserializer<Date>(Date.class) { 
    private static final long serialVersionUID = 1L; 

    @Override 
    public Date deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException { 
     JsonNode tree = jp.readValueAsTree(); 
     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 
     try { 
      // Mongo will return something that looks more like: 
      // {$date:<long integer for milliseconds>} 
      // so handle that as well. 
      JsonNode dateNode = tree.get("$date"); 
      if (dateNode != null) { 
       String textValue = dateNode.textValue(); 
       if (!Util.IsNullOrEmpty(textValue)) { 
        return ISODateTimeFormat.dateTime().parseDateTime(textValue).toDate(); 
       } 
       return Util.MillisToDate(dateNode.asLong()); 
      } 
      return null; 
     } catch (Throwable t) { 
      Util.LogIt("Exception: " + t.getMessage()); 
      throw new IOException(t.getMessage(), t); 
     } 
    } 

}); 

可以毫秒轉換爲日期或日期時間如下:

/** 
* Convert milliseconds to a date time. If zero or negative, just return 
* null. 
* 
* @param milliseconds 
* @return 
*/ 
public static Date MillisToDate(final long milliseconds) { 
    if (milliseconds < 1) { 
     return null; 
    } 
    Calendar calendar = Calendar.getInstance(); 
    calendar.setTimeInMillis(milliseconds); 
    return calendar.getTime(); 
} 

public static DateTime MillisToDateTime(final long milliseconds) { 
    if (milliseconds < 1) { 
     return null; 
    } 
    return new DateTime(milliseconds); 
}