Search code examples
javaspringmongodbmongodb-java

YearMonth field stored on mongodb cannot be parsed back to object field


Background: My application is built on top of Spring Data REST and MongoDB Repositories.

Consider this simple Java domain object with a YearMonth field:

@Getter @Setter
public class Console {
    @Id private String id;
    private String name;
    private YearMonth releaseMonth;
    private Vendor vendor;
}

And this domain object is made available for persistence by a MongoRepository implementation:

public interface ConsoleRepository extends MongoRepository<Console, String> {
    Console findByName(@Param("name") String name);
}

When exposing a REST Controller (automatically by Data REST) to manage this domain object, I've added jackson-datatype-jsr310 gradle dependency in order to parse YearMonth JSON values (e.g.: "2016-04") into this field by jackson:

compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1'

When POST'ing to this endpoint, the YearMonth value contained on the JSON document is correctly parsed into a YearMonth field and the whole object is stored as a document on MongoDB successfully. Looking up this document on mongo proves that:

> db.console.find()
{ "_id" : ObjectId("575f837ca75df1fc7e5f4f96"),
  "_class" : "xxx.yyy.Console",
  "name" : "Console 1",
  "releaseMonth" : { "year" : 1988, "month" : 10 },
  "vendor" : "VENDOR_1" }

However, when I try to GET that resource from the REST controller, MongoDB client fails to bind this YearMonth value into the Java object:

GET localhost:8080/consoles

Response:

{
  "timestamp": 1465954648903,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "org.springframework.data.mapping.model.MappingException",
  "message": "No property null found on entity class java.time.YearMonth to bind constructor parameter to!",
  "path": "/consoles"
}

I'd assume MongoDB Java client lacks built-in support for Java 8's YearMonth values, but since it is being able to save them, that seems to be ruled out. What am I missing here?


Solution

  • I was able to parse this object by creating a Custom Converter:

    @Component
    public class DBObjectToYearMonthConverter implements Converter<DBObject, YearMonth> {
        @Override
        public YearMonth convert(DBObject source) {
            return YearMonth.of(
                (int) source.get("year"),
                (int) source.get("month")
            );
        }
    }
    

    And setting up a CustomConversions @Bean on Application class:

    @Bean
    public CustomConversions getCustomConversions() {
        return new CustomConversions(Arrays.asList(
            new DBObjectToYearMonthConverter()
        ));
    }
    

    Other options are welcome.