Search code examples
springgrailsgroovydata-bindingjavabeans

Registering new default ValueConverter for Double as a bean in Grails 4


So, apparently Grails performs locale aware data binding, and as we use a danish locale as default, sending doubles as "123.123" to an endpoint converts it to "123123.0". So, I've found a few other questions regarding this and it seems like I can create a class that extends ValueConverter and explicitly convert all incoming doubles. The problem is that the convert() method never seems to be called. I saw in this post Binding real number values to Grails domain attributes (values sent by Dojo widgets), that I might have to register it as a plugin in order to specify a load order. Now I'd like to avoid that as it seems like much more work (and I've never implemented my own plugin before).

So my questions are:

  1. Do I have to register this functionality as a plugin for it to work?
  2. If no, why is it not working?
  3. Which one of my bean registration formats is correct?

Here is my converter:

import grails.databinding.converters.ValueConverter

class DoubleValueConverter implements ValueConverter {

    @Override
    boolean canConvert(Object value) {
        return value instanceof String
    }

    @Override
    Object convert(Object value) { // <- Is never called :(
        Double d = Double.parseDouble(value as String)
        return d
    }

    @Override
    Class<?> getTargetType() { // <- Is called
        return Double
    }
}

and here is resources.groovy

import dk.erst.plandata.integration.converters.DoubleValueConverter
import grails.util.Holders
import org.springframework.web.servlet.i18n.SessionLocaleResolver

beans = {

    defaultGrailsDoubleConverter (DoubleValueConverter) { bean ->
        bean.autowire = 'byName'
    }

    defaultGrailsdoubleConverter(DoubleValueConverter) { bean ->
        bean.autowire = 'byName'
    }

    "defaultGrailsjava.lang.DoubleConverter"(DoubleValueConverter) { bean ->
        bean.autowire = 'byName'
    }

    // This is the reason the converter is needed in the first place. 
    localeResolver(SessionLocaleResolver) { 
            defaultLocale = new Locale('da')
    }
}

Solution

  • Finally I found a solution to my problem. My converter is registered correct. However, it is not used as the default converter takes precedence.

    To fix this, I had to add the @Order(-1) annotation (from this issue https://github.com/grails/grails-core/issues/11404) to my DoubleValueConverter class for it to have higher precedence than the default (0?). Also, this change was introduced in Grails 4.0.2, so I had to upgrade from 4.0.1. Hope this helped someone :)