Custom converter

As you probably know VRaptor already includes some basic converters. They serve for most common task eg. conversion to String, Double or Calendar.

But sometimes you would like to instantiate one of your model objects using VRaptor. For this purpose you have to write your custom converter.

For example, imagine you would like to create a category with a sub-category and you want VRaptor to automatically create this sub-category object when sending the request.

So your model could be:

public class Category {
        
        private Integer id;
        private String name;
        private Category subCategory;   
        //getters and setters
}

In your view you could use a simple form with a combo box for the sub category id.

How does VRaptor know how to create the subCategory object? It has know idea and will throw a ConversionException. If you want VRaptor to create the subCategory, you should provide your own custom converter and tell VRaptor how to instantiate that object.

How to create your own converter

There are three steps to create you own converter.

First of all create a class which implements org.vraptor.converter.Converter:

public class CategoryConverter implements Converter {
}

Now implement the method public Class<?[] getSupportedTypes();>. It returns an array of types that this converter is able to deal with:

public class CategoryConverter implements Converter {

        public Class<?>[] getSupportedTypes() {
                return new Class[] { Category.class };
        }

}

And last of all let's implement the convertion method itself.

        /**
         * Converts a value to an specific type
         * 
         * @param value
         *            current value
         * @param type
         *            desired type
         * @return the converted value
         * @throws ConversionException
         *             some convertion problem hapenned
         */
        public Object convert(String value, Class<?> type, LogicRequest request)
                        throws ConversionException;

If, during convertion, a ConversionException is thrown, an exception will rise and be given to the servlet container.

If you want to skip this exception and enter a default value, do it yourself by returning null or something similar.

For example:

public Object convert(String categoryId, Class<?> type, LogicRequest request)
                throws ConversionException {
                
                Category category = new Category();
                try {
                        category.setId(Integer.parseInt(categoryId));                   
                } catch (NumberFormatException e) {
                        category = null;
                }
                return category;
}

How to install your converter

If you want to install a converter which has not been installed by default, simply use the vraptor.xml file.

This will register the converter in the entire system... you may read another tutorial on how to override the default converter for some specific setter.

<vraptor>

        <converter>org.vraptor.examples.converter.CategoryConverter</converter>
        
</vraptor>

If a type can be converted by two different converters, the latest registered converter is picked.

Converter instantiation

When a converter is registered it is using an specific instance, therefore that's the only instance which will ever be created (unless you do it by yourself): do not keep member variables that are read-write in your converters!

This means that a single instance of your converter should be thread safe: two threads can (and will) call your converter's method at the same time.

Note: The @Conversion annotation describes converters that will be instantiated as required. Those converters might be cached in a future release... the thumb rule is: do not use read-write member variables in your converter classes