[VRaptor3] BooleanConverter não está funcionando com Wrapper Class [RESOLVIDO]

23 respostas
lscosta

Buenas!

Caros, o BooleanConverter não está convertendo valores FALSE quando uso a wrapper class Boolean.

No controller tenho:

@Path("/level1/save")
	public void save(Level1 level1, Boolean teste1, boolean teste2) {

		System.out.println("=================================================== ");
		System.out.println(teste1 + ", " + teste2);
		System.out.println("=================================================== ");

	}

Quando submeto dois checkboxes, “teste1” e “teste2”, ambos não checkados, a saída no log é:

11:15:24,816 DEBUG [ParanamerNameProvider] Found parameter names with paranamer for Level1Controller.save(Level1, Boolean, boolean) as [level1, teste1, teste2]
11:15:24,816 DEBUG [ParametersInstantiatorInterceptor] Parameter values for [DefaultResourceMethod: Level1Controller.saveLevel1Controller.save(Level1, Boolean, boolean)] are [br.com.company.app.model.Level1@178cce2, null, false]
11:15:24,834 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor ExecuteMethodInterceptor
11:15:24,834 DEBUG [ExecuteMethodInterceptor] Invoking Level1Controller.save(Level1, Boolean, boolean)
=================================================== 
null, false
===================================================

Alguma luz?

23 Respostas

lscosta

Obs.: quando seto os checkboxes, recebo os dois parametros como true!

lscosta

Baixei o código do vraptor e inclui uns System.out.println’s nos conversores PrimitiveBooleanConverter e BooleanConverter.

Parece que só passa por alí quando seto os checkboxes (true).

G

lscosta:
Baixei o código do vraptor e inclui uns System.out.println’s nos conversores PrimitiveBooleanConverter e BooleanConverter.

Parece que só passa por alí quando seto os checkboxes (true).

Isso não é culpa do vraptor, mas sim do HTML (se é que alguém tem culpa).

Quando você tem um input-text e você não preenche ele vai como fazio, afinal o campo está lá porém vazio. Se o campo não estiver na tela o valor enviado será um null.

No caso de um checkbox diz a spec do HTML que ele apenas é enviando quando você “checar” ele. Caso você não “checar” o checkbox nenhum valor é enviado, sendo assim o vraptor não tem como saber se o campo não existe ou se ele não está marcado.

No caso do seu boolean nativo como o inicio dele é sempre false você acaba achando que ele é setado para false, mas quando na verdade ele sempre foi false.

Se você mudar seu form para GET ao invés de POST notará isso que eu te expliquei no querystring da URL. Note que quando você marca o checkbox você terá o nome e valor no querystring, porém caso você não marcar nada irá para o querystring.

boneazul

garcia-jj:
lscosta:
Baixei o código do vraptor e inclui uns System.out.println’s nos conversores PrimitiveBooleanConverter e BooleanConverter.

Parece que só passa por alí quando seto os checkboxes (true).

Isso não é culpa do vraptor, mas sim do HTML (se é que alguém tem culpa).

Quando você tem um input-text e você não preenche ele vai como fazio, afinal o campo está lá porém vazio. Se o campo não estiver na tela o valor enviado será um null.

No caso de um checkbox diz a spec do HTML que ele apenas é enviando quando você “checar” ele. Caso você não “checar” o checkbox nenhum valor é enviado, sendo assim o vraptor não tem como saber se o campo não existe ou se ele não está marcado.

No caso do seu boolean nativo como o inicio dele é sempre false você acaba achando que ele é setado para false, mas quando na verdade ele sempre foi false.

Se você mudar seu form para GET ao invés de POST notará isso que eu te expliquei no querystring da URL. Note que quando você marca o checkbox você terá o nome e valor no querystring, porém caso você não marcar nada irá para o querystring.

É checkbox é sempre um problema…

Eu adoto o padrão de iniciar com flags S ou N / 1 ou 0

Deixo o padrão na propriedade da classe.

private char gostafrutas=‘N’;

e no html

Portanto quando for o submit se ele marcou vira sim,senao marcou nada vai N como padrão.
É um modo de se resolver.

lscosta

@garcia-jj

Certo, mas a documentação do vraptor diz que há esse tratamento. E verificando o código pode-se confirmar isso. Parece apenas que não está fucionando 100%.

Imaginei que o vraptor identificasse os parâmetros do meu método e, no caso de eu esperar um Booleano que não está no request, ele seria setado como false.

Não deveria ser assim?

A única saída é mudar todos “Boolean” para “boolean”?

lscosta

@boneazul

O problema de eu setar um valor padrão no meu pojo, é que esse valor vai automaticamente ser utilizado pelo Hibernate nas pesquisas, e aí precisarei de outros ‘workarounds’…

G

Não faz isso porque Boolean.FALSE não é a mesma coisa que NULL. É como o caso de uma String vazia. String vazia é uma coisa, String nula é outra. Boolean.FALSE indica que um valor foi marcado como FALSE, porém null indica que ele não foi marcado ou até mesmo que não existe.

Se o vraptor considerar NULL sempre que o valor não vier, o que acontecerá quando você tiver um com os valores algo como:

Você tem computador em casa?

  • Sim - TRUE
  • Não - FALSE

Nesse caso se o fulano não responder a questão o vraptor magiamente vai colocar o valor como FALSE, sendo assim ele vai assumir que o cara não tem um computador em casa. Porém na verdade ele nem mesmo respondeu.

Acho que o ideal no seu caso é você usar mesmo boolean primitivo ou então fazer um tratamento para considerar Boolean.FALSE = null.

Abraços

lscosta

@garcia-jj

Boolean significa: verdadeiro OU falso. Não há terceira opção. Se houver, como no caso do seu , eu devo usar outro Tipo para armazenar esse valor, que não seja booleano.

O que você disse dá a entender que, conceitualmente, Boolean é diferente de boolean. Aí eu discordo. Eles podem ser diferentes para o Java, mas isso não muda o conceito de valor booleano.

Obviamente essa conversão de nulo para false não é aplicável à String ou sei lá que outro tipo. Estou falando apenas do caso Boolean.

Continuo achando que o Conversor do vraptor deveria fazer esse tratamento. Do contrário, não há porque haver o tratamento para false que tem lá dentro:

...
private static final Set<String> IS_FALSE = new HashSet<String>(Arrays.asList("FALSE", "0", "NO", "N", "OFF"));
...
Lucas_Cavalcanti

a partir do momento em que Boolean aceita null, eu não posso simplesmente supor que qdo não vir o parâmetro na requisição o boolean fica false…

imagina que vc tem um campo boolean opcional no seu banco de dados… na sua classe do hibernate ele vai ser Boolean… e se eu não preenchê-lo na requisição, ele tem que continuar opcional, ou seja, ele tem que ser null…

isso é meio que culpa do java, mas não sei se a gente pode mudar o comportamento do boolean agora…

de qqer forma, vc resolve esse problema com essa classe simples:

@Convert(Boolean.class)
public class CustomBooleanConverter extends BooleanConverter {
   public Boolean convert(String value, ...) {
      if (value == null) return Boolean.FALSE;
      return super.convert(value, ...);
   }
}
G

lscosta, você está certo em dizer que boolean é true ou false, mas e quando esse campo é opcional? Na verdade se você usar a wrapper você tem trẽs valores: true, false ou nada. Essa é exatamente a diferença entre boolean e Boolean. A wrapper te permite você ter um valor concreto ou NENHUM valor. Mas porque é assim tão complicado você usar o primitivo já que você nunca terá NULL?

Ahh, legal. Assim eu então nos meus sistemas que eu tenho, como no exemplo que te dei, a opção de true, false ou nada? Como faço para indicar que aquele campo é opcional e o cliente não preencheu?

Há questões que existem apenas um SIM ou NAO, mas e nas questões do tipo “Não respondido?”.

Lucas, você consegue explicar as coisas melhor que eu, hein? Acho que preciso parar de ler muitos livros técnicos. Acho que a faculdade de engenharia não me ajudou nada, hahahaha.

Qual é a culpa do Java nisso? O cara está confundindo wrapper com primitivo, onde um objeto pode não possui valor algum (referencia para null). Não vejo culpa alguma do Java nisso.

Lucas_Cavalcanti

ô.ó
pq esse ódio gratuito? tinha que ser engenheiro mesmo :wink:

garcia-jj:

Qual é a culpa do Java nisso? O cara está confundindo wrapper com primitivo, onde um objeto pode não possui valor algum (referencia para null). Não vejo culpa alguma do Java nisso.


a culpa do java é ter tentado ser “backward compatible” com C++ qdo foi criado… não deveriam haver tipos primitivos… tudo deveria ser objeto e ponto, pra não haver esse tipo de confusão… mas enfim… isso é outra discussão…

boneazul

lscosta:
@boneazul

O problema de eu setar um valor padrão no meu pojo, é que esse valor vai automaticamente ser utilizado pelo Hibernate nas pesquisas, e aí precisarei de outros ‘workarounds’…

Bom vi que voce vai conseguir usar do seu modo pelo wrapper do lucas , mas algo que não entendi…
Como assim utilizado automaticamente nas pesquisas??

Outra coisa a partir do momento que voce coloca um “checkbox” numa aplicação , por conceito do objeto a que ele se propoe a fazer se não checkado ele tem q assumir um valor…se o cara nao marcou é pq ele nao quer algo que voce propos para ele…não é legal voce assumir “nada” ou “null” como valor…pq quando voce colocar um valor null voce ta falando que

S é contrario de null / 1 é contratio de null / true é contrario de null quando deveria pensar que S contrario de N 1 de 0 e por ai vai…

Logico que isso é discutivel…mas eu penso desse modo…

lscosta

Ao se usar checkboxes para armazenar campos booleanos, esse comportamento é óbvio:

  • checkbox desmarcado = false = Boolean.FALSE

  • checkbox marcado = true = Boolean.TRUE

Quando eu não marco um checkbox, não estou me abstendo da resposta (informando null). Estou respondendo “não” (false).

O problema que é: como saber que essa informação (não) veio de um maldito checkbox?

Concordo com o Lucas quando diz:

Lucas Cavalcanti:
a partir do momento em que Boolean aceita null, eu não posso simplesmente supor que qdo não vir o parâmetro na requisição o boolean fica false…

Mas tenho que ter uma convenção, não quero ficar tratando de modo diferente cada atributo booleano do sistema.

Vou sobrescrever o conversor considerando a string “null”, para poder usar combos como o pessoal comentou.

@Convert(Boolean.class)  
public class CustomBooleanConverter extends BooleanConverter {  
   public Boolean convert(String value, ...) {  

      if (value == null) return Boolean.FALSE;  

      if ("null".equals(value)) return null;  

      return super.convert(value, ...);  
   }  
}

E no HTML:

<select>
  <option value="null">Não informado</option>
  <option value="true">Sim</option>
  <option value="false">Não</option>
</select>

Acho que assim consigo considerar todos pontos levantados nessa thread. Concordam com isso?

Valeu pela discussão gurizada!

lscosta

boneazul:
lscosta:
@boneazul

O problema de eu setar um valor padrão no meu pojo, é que esse valor vai automaticamente ser utilizado pelo Hibernate nas pesquisas, e aí precisarei de outros ‘workarounds’…

Bom vi que voce vai conseguir usar do seu modo pelo wrapper do lucas , mas algo que não entendi…
Como assim utilizado automaticamente nas pesquisas??


Por exemplo, usando o Hibernate:

dao.findByExample(new Cliente());

Se houverem atributos com valor default na classe Cliente, esse resultado vai ser automagicamente filtrado por eles…

Lucas_Cavalcanti

se essa é a sua convenção e ela é diferente da do VRaptor, vá em frente… sobrescreva a convenção do vraptor, é fácil e rápido… =)

lscosta

Mais um bom motivo pra continuar usando o vraptor nos próximos projetos! :wink:

valeu!

lscosta

Ooops!

Lucas,

o conversor não é acionado se o valor enviado é nulo, ou seja, “value == null”

Se informo os checkboxes ele funciona corretamente. Havia comentado isso na sexta-feira, mas acabei esquecendo hoje… =P

E agora?

Lucas_Cavalcanti

pois eh… se vc não marca o checkbox ele não vai pra requisição… não tem o que fazer, pelo menos do lado do vraptor…

do lado do cliente, dá pra vc fazer um javascriptzinho que resolve esse problema… com jquery:

$(function() {
  //submit de todos os formularios, vc pode limitar pra um específico
  $('form').submit(function() {
     //todo checkbox que não estiver checado, checa e troca o valor pra false
     $(':checkbox:not(:checked)').attr('checked', 'checked').val('false');
     return true; // pro form submeter
  });
});

algo parecido com isso deve resolver o problema

G

lscosta, agora entendi o motivo de você usar o campo como Wrapper, já que você está usando o findByExample do Hibernate.

Já que no seu caso você usa sua própria entidade para pesquisa, o que normalmente não faço nos meus projetos, creio que um bom workaround é você então, quando atualizar o registro, atualize o campo para Boolean.FALSE caso o campo for null.

Digamos que você tenha no cadastro de um cliente a opção “Você quer débito em conta?”. Para isso não existe NULL, FALSE e TRUE, apenas TRUE e FALSE já que o cliente pode optar apenas por um SIM ou NAO. No caso não selecionando você quer considerar um NAO. No seu método de inclusão/atualização do cadastro do cliente faça a validação: se o campo isDebitoEmConta = null assime isDebitoEmConta = false.

lscosta

@garcia-jj

Foi o que acabei fazendo!

Estou procurando evitar qualquer coisa que dê margem à falha do desenvolvedor, mas nesse caso não vi muita saída.

Ao menos quando o campo booleano for not null na base, o cara vai ter que ter o cuidado de fazer essa validação ao salvar…

Valeu!

lscosta

Um colega apontou ainda outra opção, que seria usar dois componentes radio em vez de checkbox.

Em vantagem ao uso de um combo estaria o menor número de clicks e melhor visualização…

Enfim, é uma outra alternativa.

Lucas_Cavalcanti

vc considerou o javascript que eu te mandei?

lscosta

@Lucas

=D

Sinceramente, naquele momento achei que não fosse uma boa solução! Mas é!

Achei que teria problema com os formulários de pesquisa e nem pensei muito, mas estou usando combos para isso (todos, true, false).

Perfeito, brother! Refatorado, testado e aprovado!

Valeu!

Criado 9 de abril de 2010
Ultima resposta 14 de abr. de 2010
Respostas 23
Participantes 4