Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring type conversion not taking effect

I have a lot of Enums implementing an Interface called Codeable. I want to do a reverse look up when deserializing from json and trying to use a ConverterFactory

public class StringToCodeableConverterFactory implements ConverterFactory<String, Codeable> {

@Override
public <T extends Codeable> Converter<String, T> getConverter(
        Class<T> targetType) {
    return new StringToCodeableConverter<T>(targetType);
}

private final class StringToCodeableConverter<T extends Codeable> implements Converter<String, T> {

    private Class<T> enumType;

    public StringToCodeableConverter(Class<T> enumType) {
        this.enumType = enumType;
    }

    @Override
    public T convert(String source) {
        return CodeableUtil.get(this.enumType, source);
    }

}

}

Here's the spring config

<!-- Custom Converters from String to Java Type-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="com.duetto.model.StringToCodeableConverterFactory" />
        </list>    
    </property>
</bean>

<mvc:annotation-driven conversion-service="conversionService">

After some digging, I figured out Spring is taking the default StringToEnumConverterFactory instead of my StringToCodeableConverterFactory, why is it this way? How can I make my take precedence over the other one?

like image 998
user1322614 Avatar asked Oct 23 '25 15:10

user1322614


1 Answers

What I noticed happening was that the DefaultConversionService's defaults included a StringToEnumConversion which appears first in the service's List of possible converters. As such, mine was never being hit, and the standard Enum conversion was being attempted every time.

My workaround was to:

  1. Unregister the default string-to-enum converter - registry.removeConvertible(String.class, Enum.class) where registry is an instance of FormatterRegistry
  2. Write a string-to-mycustomenum converter
  3. Write an all-encompassing string-to-enum converter that did a type check (MyEnumType.class.isAssignableFrom(targetType)) and delegated to either my custom converter or the default string-to-enum converter depending on the result

Note this approach has several problems, among them: StringToEnumConverter is a package-private class so I had to copy-paste it into my own code base. Additionally, this can't be the desired approach to solving this problem; it isn't very "springy".

Would love to hear alternative answers for this.

Worth noting, I'm using Spring 3.2.6

Major Update

I found a much cleaner way, note that I'm using annotation config rather than xml but the principals should be the same.

In Spring's documentation, I found:

GenericConversionService is a generic implementation designed to be explicitly configured, either programatically or declaratively as a Spring bean. DefaultConversionService is a subclass that pre-registers the common Converters in the core.converter package as a convenience.

So, I now have overrides configured as follows:

@Override
public FormattingConversionService mvcConversionService() {
    // use FormattingConversionService here rather than GenericFormattingConversionService (the default) 
    // because it does not automatically register defaults
    FormattingConversionService conversionService = new FormattingConversionService();
    addFormatters(conversionService);
    return conversionService;
}

@Override
protected void addFormatters(FormatterRegistry registry) {
    // register custom enum handler first
    registry.addConverterFactory(new MyCustomEnumConverterFactory());
    // now add in spring's defaults
    DefaultConversionService.addDefaultConverters(registry);
    DefaultFormattingConversionService.addDefaultFormatters(registry);
}

Now everything's working and it feels significantly less hacky.

like image 95
drobert Avatar answered Oct 26 '25 19:10

drobert



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!