VRaptor

When to use a Converter?

When a parameter is received, we need to convert to an Object. VRaptor already have registered converters for all types define in Java Language Specification. If the type is primitive and parameter is null, the default value for the type will be returned. Example: if your type is an int and the value is null, the value 0 will be returned. For the non-primitive types the null value is returned if the parameter is null or empty.

For example, if we receive the parameters:

client.id 10
client.age null

for the class below:

public class Client {
    private Long id;
    private int age;

    // getters and setters
}

When inject the parameter data, VRaptor will call LongConverter for the field id and PrimitiveIntConverter for the field age. In this case id will receive value 10, and age will receive value 0 because parameter is null.

You can see a complete list with all available converters here.

BigDecimal, Double and Float with localization

Are supported using application locale. Double and Float supports both primitive and non-primitive types.

Working with Date and Calendar

Calendar and Date are supported using application locale.

There are converters for the main Joda-time types: DateTime, LocalDate, LocalDateTime and LocalTime available using the vraptor-jodatime plugin.

If you are using JDK 8, you can use the new java.time API available using vraptor-java8 plugin.

If conversion fails

When a conversion fails VRaptor will add the error message using localization. To see these messages you need to add the lines below in your resource bundle:

is_not_a_valid_number = {0} is not a valid number.
is_not_a_valid_integer = {0} is not a valid integer.
is_not_a_valid_character = {0} is not a valid character.
is_not_a_valid_enum_value = {0} is not a valid option.
is_not_a_valid_date = {0} is not a valid date.
is_not_a_valid_boolean = {0} is not a valid boolean. Please use true/false, yes/no, y/n or on/off
is_not_a_valid_time = {0} is not a valid time.
is_not_a_valid_datetime = {0} is not a valid datetime.

Changing your Locale

The converters uses the JVM’s default locale. You can override the JVM locale adding these lines in your web.xml:

<context-param>
    <param-name>javax.servlet.jsp.jstl.fmt.locale</param-name>
    <param-value>en_US</param-value>
</context-param>

Or you can start your application server or servlet container using the VM parameter:

-Duser.language=en -Duser.region=US

Creating your own converter

All converters must implement VRaptor’s Converter interface. The concrete class will define which type it is able to convert, and will be invoked with a request parameter, the target type and a resource bundle containing i18n messages, useful if you wish to raise a ConversionException in case of convertion errors.

public interface Converter<T> {
    T convert(String value, Class<? extends T> type);
}

Also you must tell VRaptor (not the compiler) which type your converter is able to handle. You do that by annotating your converter class with @Convert:

@Convert(Long.class)
public class LongConverter implements Converter<Long> {
    // your code here
}

Finally, don’t forget to specify the scope of your converter, just like you do with any other resource in VRaptor. For example, if your converter doesn’t need any user specific information, it can be registered as application scoped and only one instance of that converter will be created:

@Convert(Long.class)
@ApplicationScoped
public class LongConverter implements Converter<Long> {
    // your code here
}

In the following lines, you can see a LongConverter implementation, showing how simple it is to assemble all the information mentioned above:

@Convert(Long.class)
@ApplicationScoped
public class LongConverter implements Converter<Long> {

    public Long convert(String value, Class<? extends Long> type) {
        if (isNullOrEmpty(value)) {
            return null;
        }

        try {
            return Long.valueOf(value);
        } catch (NumberFormatException e) {
            throw new ConversionException(new ConversionMessage("is_not_a_valid_integer", value));
        }
    }
}

Setting nested properties

In the previous examples, we learn how to convert a request parameter to an object. For example, a parameter country can converted to a object Country. But there are some cases that you want to only setting a nested property, like a country name.

For this case, if you have a request parameter country.name, you can create the converter below to instantiate a new Country instance only with name setted.

@Convert(Country.class)
@ApplicationScoped
public class CountryConverter implements Converter<Country> {

    public Country convert(String value, Class<? extends Country> type) {
        Country country = new Country();
        if (!isNullOrEmpty(value)) {
            country.setName(value)
        }

        return country;
    }
}

Overriding core converters

You can also overrise any existing converters. You only need extends the core converter, after to add @Specializes annotation and @Convert with the specialized type. Below you can see an example how to override a converter BigDecimalConverter:

@Specializes
@Convert(BigDecimal.class)
public class CustomBigDecimalConverter extends BigDecimalConverter {
    
    @Override
    public BigDecimal convert(String value, Class<? extends BigDecimal> type) {
        // your code here
        return super.convert(value, type);
    }
}

You can use CDI’s @Specializes always you desire to reuse the superclasse behavior, otherwise you can create an @Alternative with higher priority than VRaptor implementation, i.e:

@Alternative
@Priority(Interceptor.Priority.APPLICATION)
@Convert(BigDecimal.class)
public class CustomDecimalConverter implements Converter<BigDecimal>{

    @Override
    public BigDecimal convert(String value, Class<? extends BigDecimal> type) {
        return // your code here
    }
}

All of VRaptor’s Converters have LIBRARY_BEFORE priority.

Overriding custom JSON and XML converters

You can also customize the Date pattern by implementing your own CustomDateGsonConverter and overriding the method getPattern():

@Specializes
public class CustomDateGsonConverter extends DateGsonConverter {
    @Override
    public DateFormat getPattern() { return //any custom pattern }
}