Search code examples
javagrailsgrails-ormgrails-domain-classjava-time

Could not find matching constructor for: java.time.LocalDateTime(java.lang.String)


I have a Grails 3.1.2 application

One of my domain classes is Goal

import java.time.LocalDateTime
import java.time.temporal.ChronoUnit

class Goal {
    String name
    String description
    LocalDateTime targetDate
    Date dateCreated
    Date lastUpdated
    static constraints = {
        name(nullable: false, blank: false)
    }
}

When I call validate on my an instance of Goal I get:

  • Field error in object 'com.liftyourgame.model.Goal' on field 'targetDate': rejected value [2016-01-28T15:10:39.000Z]; codes [com.liftyourgame.model.Goal.targetDate.typeMismatch.error,com.liftyourgame.model.Goal.targetDate.typeMismatch,goal.targetDate.typeMismatch.error,goal.targetDate.typeMismatch,typeMismatch.com.liftyourgame.model.Goal.targetDate,typeMismatch.targetDate,typeMismatch.java.time.LocalDateTime,typeMismatch]; arguments [targetDate]; default message [Could not find matching constructor for: java.time.LocalDateTime(java.lang.String)]

How can I change the validation so it doesn't need this constructor?


Solution

  • How can I change the validation so it doesn't need this constructor?

    You can't, but the validation isn't really the problem. The data binder is the problem. The default binder does not yet have built in support for LocalDateTime.

    You can register your own converter like this:

    Converter:

    // src/main/groovy/demo/MyDateTimeConverter.groovy
    package demo
    
    import grails.databinding.converters.ValueConverter
    
    import java.time.LocalDateTime
    import java.time.format.DateTimeFormatter
    
    class MyDateTimeConverter implements ValueConverter {
        @Override
        boolean canConvert(Object value) {
            value instanceof String
        }
    
        @Override
        Object convert(Object value) {
            def fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
            LocalDateTime.parse(value, fmt)
        }
    
        @Override
        Class<?> getTargetType() {
            LocalDateTime
        }
    }
    

    Register it as a bean:

    // grails-app/conf/spring/resources.groovy
    beans = {
        myConverter demo.MyDateTimeConverter
    }