5
votes

I am using the java 8 java.time.LocalDate to parse around dates.

But trying to insert a LocalDate object to mongodb. I get errors in the java driver:

private def writeData(measure: DBCollection, installation: Int, date: String, dates: ListBuffer[LocalDate],
                    values: ListBuffer[BigDecimal], validated: Boolean, overwrite: Boolean) {
  val timeValues: BasicDBList = new BasicDBList
  var i = 0
  while ( i < dates.size )  {
    val obj: BasicDBObject = new BasicDBObject("time", dates(i))
    obj.put("value", values(i).toString())
    timeValues.add(obj)
    i += 1
  }
  if ( debug ) System.out.println("Storedata: " + timeValues.toString) <-- error here

Errorlog:

java.lang.RuntimeException: json can't serialize type : class java.time.LocalDate at com.mongodb.util.ClassMapBasedObjectSerializer.serialize(ClassMapBasedObjectSerializer.java:77) at com.mongodb.util.JSONSerializers$MapSerializer.serialize(JSONSerializers.java:317) at com.mongodb.util.ClassMapBasedObjectSerializer.serialize(ClassMapBasedObjectSerializer.java:79) at com.mongodb.util.JSONSerializers$IterableSerializer.serialize(JSONSerializers.java:290) at com.mongodb.util.ClassMapBasedObjectSerializer.serialize(ClassMapBasedObjectSerializer.java:79) at com.mongodb.util.JSON.serialize(JSON.java:54) at com.mongodb.util.JSON.serialize(JSON.java:40) at com.mongodb.BasicDBList.toString(BasicDBList.java:38) at web.MeasureAccess.writeData(MeasureAccess.scala:203) at web.MeasureAccess.firstTime(MeasureAccess.scala:52) at web.MeasureAccess$.main(MeasureAccess.scala:262) at web.MeasureAccess.main(MeasureAccess.scala) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

I am using the mongo-java-driver-2.13.0-rc1.jar Scala 2.11.4 and java 1.8.0_25

For completeness.

3

3 Answers

9
votes

Unfortunately, the MongoDB driver uses the java.util.Date type, see the docs here

So you have to convert your LocalDate to a Date instance first, for example:

MongoClient mongoClient = new MongoClient("localhost", 27017);
DB db = mongoClient.getDB("test");
DBCollection coll = db.getCollection("testcol");

LocalDate ld = LocalDate.now();
Instant instant = ld.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
Date date = Date.from(instant);

BasicDBObject doc = new BasicDBObject("localdate", date);
coll.insert(doc);

I would suggest using something like Morphia or Jongo to wrap the MongoDB driver though, as you can register global mappers to implicitly do these conversions on the fly, so that you can use LocalDate, etc, in your domain model

4
votes

For anyone coming across this question, the following converter would get someone started in the right direction. This TypeConverter converts between Date and LocalDateTime (Making this distinction because the OP specifically asked about LocalDate).

Add the following converter to Morphia as follows:

morphia.getMapper().getConverters().addConverter(new LocalDateTimeConverter());

Here is the converter class:

public class LocalDateTimeConverter extends TypeConverter implements SimpleValueConverter {

    public LocalDateTimeConverter() {
        // TODO: Add other date/time supported classes here
        // Other java.time classes: LocalDate.class, LocalTime.class
        // Arrays: LocalDateTime[].class, etc
        super(LocalDateTime.class);
    }

    @Override
    public Object decode(Class<?> targetClass, Object fromDBObject, MappedField optionalExtraInfo) {
        if (fromDBObject == null) {
            return null;
        }

        if (fromDBObject instanceof Date) {
            return ((Date) fromDBObject).toInstant().atZone(ZoneOffset.systemDefault()).toLocalDateTime();
        }

        if (fromDBObject instanceof LocalDateTime) {
            return fromDBObject;
        }

        // TODO: decode other types

        throw new IllegalArgumentException(String.format("Cannot decode object of class: %s", fromDBObject.getClass().getName()));
    }

    @Override
    public Object encode(Object value, MappedField optionalExtraInfo) {
        if (value == null) {
            return null;
        }

        if (value instanceof Date) {
            return value;
        }

        if (value instanceof LocalDateTime) {
            ZonedDateTime zoned = ((LocalDateTime) value).atZone(ZoneOffset.systemDefault());
            return Date.from(zoned.toInstant());
        }

        // TODO: encode other types

        throw new IllegalArgumentException(String.format("Cannot encode object of class: %s", value.getClass().getName()));
    }
}
1
votes

As Oliver Gierke mentions here

This datatype is not supported yet. I hope this will be available soon.