How to store Date field as ISODate() using jackson in MongoDb

What you need is the Jackson Joda Module. If you import that into your classpath, you can do the following on your mapper to write it as your desired Timestamp:

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

You can replace date in the code sample above with your POJO as necessary.

Edit: It looks like what you really want is a custom serializer. That would look something like this:

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 + "\")");
    }

Then you'll either register it on the mapper for all DateTime types

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

or specify it on the function using annotations

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

None of these answers accomplished what I wanted. I was having trouble because when I serialized the JSON string to MongoDB, it was stored as a String. A nicely formatted string, but a string none the less.

I use the com.fasterxml.jackson.databind.ObjectMapper to convert my objects to/from JSON and I wanted to continue using this class. I have the following method:

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;
}

Now, I can run the following test code:

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);

The output is as follows:

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

Note that the JSON that will be sent to MONGODB defines the value containing a field named "$date". This tells MongoDB that this is a date object it seems.

When I look at Mongo, I see the following:

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

Now, I can access the field as a date rather than as a string.

To add an encoded JSON string to Mongo, my code is as follows:

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

In this case, "json" is the encoded JSON string. Because it is coming from a JSON string, it has no way of knowing the types unless it infers this, which is why we needed the "$date" portion. The "bucket" is just a string indicating which table to use.

As a side note, I found out that if I pull a BSON object from Mongo and convert it to a JSON string by calling doc.toJson() (where doc is of type org.bison.Document as returned from a query), the date object is stored with a long value rather than a formatted text string. I did not check to see if I could push data into mongo after formatting in this way, but, you can modify the deserializer shown above to support this as follows:

    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);
        }
    }

});

You can convert milliseconds to a Date or DateTime as follows:

    /**
 * 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);
}

I was able to serialize the date string to ISODate format. I wrote a customer date serializer like below.

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

Based on request from user @mmx73, I am adding code for customer 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;
    }
}