Quando usar um Conversor?
Quando recebemos um parametro do browser, recebemos como uma String. Por isso é necessário um conversor para fazer a tradução deste parametro para seu respectivo objeto. O VRaptor já possui registrado conversores para todos os tipos definidos na Java Language Specification
. Para os tipos primitivos, caso o parâmetro seja recebido como null
, será retornado o valor padrão do campo. Por exemplo, se você receber um int
cujo valor seja null
, o converter irá retornar 0
. Já para os tipos objetos, se o parâmetro recebido for null
ou vazio, o valor retornado será null
.
Por exemplo, se recebermos os seguintes parâmetros do browser:
cliente.id | 10 |
cliente.idade | null |
para a classe abaixo:
public class Cliente {
private Long id;
private int idade;
// getters and setters
}
Ao injetar os dados da requisição na classe, o VRaptor irá usar o conversor LongConverter
para o campo id
e o conversor PrimitiveIntegerConverter
para o campo idade
. Neste caso, o atributo id
receberá o valor 10
, e o campo idade
receberá o valor 0
, pois o parametro recebido é null
.
Você pode obter a lista completa dos conversores padrão do vraptor aqui.
BigDecimal, Double e Float localizados
São suportados utilizando o padrão de localização da sua aplicação. Double
e Float
são suportados tanto o tipo primitivo quanto o tipo objeto.
Trabalhando com datas e calendários
Calendar
e Date
são suportados por padrão usando a localização da sua aplicação.
Existem conversores para os principais tipos do Joda-time como DateTime
, LocalDate
, LocalDateTime
e LocalTime
. Os conversores do Joda-time estão disponíveis usando o plugin vraptor-jodatime.
Se você está usando JDK 8, você pode tirar proveito dos conversores do pacote java.time
, que estão disponíveis usando o plugin vraptor-java8.
Mensagem de erro de conversão
Quando o VRaptor tenta converter um objeto, e ocorrer algum erro, é retornada para a apicação uma exception indicando o erro de conversão. Por exemplo, se você receber um parametro com valor A
para um campo Integer
, a conversão irá falhar e você receberá uma ConversionException
internacionalizada. Para exibir as mensagens na sua aplicação, basta adicionar as seguintes chaves no seu resource bundle:
is_not_a_valid_number = {0} não é um número válido.
is_not_a_valid_integer = {0} não é um inteiro válido.
is_not_a_valid_character = {0} não é um caracter válido.
is_not_a_valid_enum_value = {0} não é uma opção válida.
is_not_a_valid_date = {0} não é uma data válida.
is_not_a_valid_boolean = {0} não é um valor boolean válido.
is_not_a_valid_time = {0} não é um horário válido.
is_not_a_valid_datetime = {0} não é uma data e horário válidos.
Definindo a localização (Locale) da aplicação
A localização dos componentes pode ser alterada utilizando a seguinte configuração no web.xml:
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.locale</param-name>
<param-value>pt_BR</param-value>
</context-param>
Ou vocẽ pode iniciar o servidor de aplicações ou servlet container com o parâmetro:
-Duser.language=pt -Duser.region=BR
Criando seu próprio converter
Todos os conversores devem implementar a interface Converter
do vraptor. A classe concreta definirá o tipo que ela é capaz de converter e será invocada com o valor do parâmetro do request e o tipo alvo.
public interface Converter<T> {
T convert(String value, Class<? extends T> type);
}
Além disso, seu conversor deve ser anotado com o tipo que seu conversor é capaz de converter, para isso utilize a anotação @Convert
:
@Convert(Long.class)
public class LongConverter implements Converter<Long> { ... }
Por fim, lembre-se de dizer se seu conversor pode ser instanciado em um escopo de Application
, Session
ou Request
, assim como recursos e componentes quaisquer do VRaptor. Por exemplo, um conversor que não requer nenhuma informação do usuário logado pode ser registrado no escopo de Application
e instanciado uma única vez:
@Convert(Long.class)
@ApplicationScoped
public class LongConverter implements Converter<Long> { ... }
A seguir, a implementação de LongConverter
mostra como tudo isso pode ser utilizado de maneira bem simples:
@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));
}
}
}
Populando outras propriedades de um objeto convertido
Nos exemplos acima, usamos os converters para converter um parâmetro do request em um objeto, por exemplo, parâmetro pais
converter em um objeto Pais
. Porém há casos onde precisamos apenas criar um objeto com alguma propriedade populada. Exemplo: para o parametro pais.nome
podemos ter um objeto Pais
com o atributo nome
preenchido.
Neste caso, o value
recebido no método será o valor do atributo nome
. Basta então criar um converter como no exemplo abaixo:
@Convert(Pais.class)
@ApplicationScoped
public class PaisConverter implements Converter<Pais> {
public Pais convert(String value, Class<? extends Pais> type) {
Pais pais = new Pais();
if (!isNullOrEmpty(value)) {
pais.setNome(value)
}
return pais;
}
}
Sobrescrevendo o comportamento dos conversores existentes
Você pode sobrescrever qualquer converter já existente no VRaptor. Para isso basta estender a classe anotando-a com @Specializes
e também adicionando a anotação @Convert
com o tipo que será especializado. Um bom exemplo para isso é sobrescrever o conversor BigDecimalConverter
para alterar o formato do valor.
@Specializes
@Convert(BigDecimal.class)
public class MeuBigDecimalConverter extends BigDecimalConverter {
@Override
public BigDecimal convert(String value, Class<? extends BigDecimal> type) {
// seu código aqui
return super.convert(value, type);
}
}
Você pode utilizar o @Specializes
sempre que quiser reaproveitar a implementação da superclasse
, caso contrário poderá criar um @Alternative
com prioridade maior que a implementação do VRaptor. Como por exemplo:
@Alternative
@Priority(Interceptor.Priority.APPLICATION)
@Convert(BigDecimal.class)
public class MeuBigDecimalConverter implements Converter<BigDecimal>{
@Override
public BigDecimal convert(String value, Class<? extends BigDecimal> type) {
return // seu código aqui
}
}
Por padrão todo Converter
do VRaptor tem prioridade LIBRARY_BEFORE
.
Sobrescrevendo conversores de JSON e XML customizados
Você ainda pode customizar o pattern de uma Date
implementando um CustomDateGsonConverter
e sobrescrevendo o método getPattern()
:
@Specializes
public class CustomDateGsonConverter extends DateGsonConverter {
@Override
public DateFormat getPattern() { return //qualquer padrão customizado }
}