On a recent project, we ran across an interesting problem concerning a popular java mapping library. The library we were using was Dozer, a very powerful and popular mapping utility. Our hope was that it would make our code much cleaner and have a central place to handle all of our mappings to where we could quickly make changes easily and often, if needed. This is exactly what we got!

However, we did have one issue with the mapping library. At the time, there was no mapping for enumeration to Strings and vice versa. This was a huge issue for us as both systems (we were working on a middleware application) that we were talking to used enumerations heavily.

Thankfully, Dozer has a way to implement Custom Converters. Below is the custom converter code that I wrote to alleviate this issue:

package com.captech.utils.dozer.converters;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
 
import org.apache.commons.lang3.text.StrBuilder;
import org.dozer.CustomConverter;
import org.dozer.MappingException;
 
public class EnumStringBiDirectionalDozerConverter implements CustomConverter{
 
	@Override
 
	public Object convert(Object destination, Object source, Class<??> destinationClass, Class<??> sourceClass) {
 
		if(source == null)
 
			return null;
 
		if(destinationClass != null){
 
			if(destinationClass.getSimpleName().equalsIgnoreCase("String")){
 
				return this.getString(source);
 
			}else if( destinationClass.isEnum()){
 
				return this.getEnum(destinationClass, source);
 
			}else{
 
				throw new MappingException(new StrBuilder("Converter ").append(this.getClass().getSimpleName())
 
						 .append(" was used incorrectly. Arguments were: ")
 
						 .append(destinationClass.getClass().getName())
 
						 .append(" and ")
 
						 .append(source).toString());
 
			}
 
		}
 
		return null;
 
	}
 
 
 
	private Object getString(Object object){
 
		String value = object.toString();
 
		return value;
 
	}
 
	private Object getEnum(Class<??> destinationClass, Object source){
 
		Object enumeration = null;
 
 
 
		Method [] ms = destinationClass.getMethods();
 
		for(Method m : ms){
 
			if(m.getName().equalsIgnoreCase("valueOf")){
 
				try {
 
					enumeration = m.invoke( destinationClass.getClass(), (String)source);
 
				}
 
				catch (IllegalArgumentException e) {
 
					e.printStackTrace();
 
				}
 
				catch (IllegalAccessException e) {
 
					e.printStackTrace();
 
				}
 
				catch (InvocationTargetException e) {
 
					e.printStackTrace();
 
				}
 
				return enumeration;
 
			}
 
		}
 
		return null;
 
	}
 
 
 
}

This class is pretty simple. There are 3 methods including the "hook" method (convert) which will be called for all Enum to String or String to Enum mappings.

The last method listed in the class is the one we need to pay close attention to. This is the getEnum method. This method maybe the reason why this type of mapping was not included in Dozer as it requires a little bit of Java reflection. Within this method we do the following:

  1. Grab all of the methods associated with the enumeration object.
  2. Loop through all of the enumeration methods
    1. Check the current method to see if the method is the "famed" enumeration method "valueOf"
      1. If this is the method then we need to invoke this method to get the enumeration using the String that is being converted as the value to be converted
      2. return the enumeration back to the method

The next step is to configure Dozer to use this mapping. Create a file on your classpath called dozer-config.xml (it can be any name you like):

<>>
 <>>
 <> type="com.captech.utils.dozer.converters.EnumStringBiDirectionalDozerConverter">
 <>>java.lang.Enum>
 <>>java.lang.String>
 >
 >
>

Where the converter type is the full class name and class-a and class-b are the types that can be associated with this conversion.

Now we just have to configure Dozer to pick up this configuration xml. It's as simple as making sure that the xml is on the classpath. However, if you are using Spring please refer to the documentation to set up your configuration. Spring Configuration

Note: When I originally solved this issue, I posted it on Stack Overflow to help others who were having the same issue, but wanted to go into the problem we needed to solve a tad bit more and also give a little bit more of an explanation of what is going on within the custom converter.