Passar array de id para o vRaptor

32 respostas
D

Pelo que li eu poderia submeter vários campos do formulário com mesmo nome que o vRaptor trata como um array no método do controller. Exemplo:

&lt;input type="checkbox" name="id" value="1" /&gt; <br/> &lt;input type="checkbox" name="id" value="2" /&gt; <br/> &lt;input type="checkbox" name="id" value="3" /&gt; <br/> &lt;input type="checkbox" name="id" value="4" /&gt; <br/>

MeuController { public void delete( long[] id ) { ... } }

Acontece que, quando submeto isto, o argumento é nulo. Se coloco índice no “name” do campo dá IndexOutOfBoundException.

Se mudo o argumento para um List<Long>, e uso o índice nos campos, ai funciona.

&lt;input type="checkbox" name="id[0]" value="1" /&gt; <br/> &lt;input type="checkbox" name="id[1]" value="2" /&gt; <br/> &lt;input type="checkbox" name="id[2]" value="3" /&gt; <br/> &lt;input type="checkbox" name="id[3]" value="4" /&gt; <br/>

MeuController { public void delete( List&lt;Long&gt; id ) { ... } }

Qual deve ser o problema? Onde estou errando?

32 Respostas

B

tenta isso

<input type="checkbox" name="id[]" value="1" /> <br/>  
<input type="checkbox" name="id[]" value="2" /> <br/>  
<input type="checkbox" name="id[]" value="3" /> <br/>  
<input type="checkbox" name="id[]" value="4" /> <br/>
D

java.lang.ArrayIndexOutOfBoundsException java.lang.reflect.Array.set(Native Method) ognl.ArrayPropertyAccessor.setProperty(ArrayPropertyAccessor.java:121) ognl.OgnlRuntime.setProperty(OgnlRuntime.java:2225) ognl.ASTProperty.setValueBody(ASTProperty.java:127) ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220) ognl.SimpleNode.setValue(SimpleNode.java:279) ognl.ASTChain.setValueBody(ASTChain.java:227) ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220) ognl.SimpleNode.setValue(SimpleNode.java:279) ognl.Ognl.setValue(Ognl.java:737) ognl.Ognl.setValue(Ognl.java:783) br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createViaOgnl(OgnlParametersProvider.java:132)

Se o método receber um long[ ].

L

tem algum motivo forte pra usar array ao inves de List?

usar id[0], id[1], etc e long[] deveria funcionar sem problemas

D

O motivo forte é que o framework diz suportar.

L

e suporta, se vc usar direito :wink:

usando os indices e long[] funciona (o array vai ser nulo se vc não selecionar nenhum)

<input type="checkbox" name="id[0]" value="1" /> <br/>  
<input type="checkbox" name="id[1]" value="2" /> <br/>  
<input type="checkbox" name="id[2]" value="3" /> <br/>  
<input type="checkbox" name="id[3]" value="4" /> <br/>

se tanto faz um List ou um array, use List

D

Provavelmente vocês atualizaram a documentação, mas havia um trecho que orientava a fazer da maneira que fiz com array e usando índica para listas.

D

My bad! Na verdade eu tinha visto aqui: http://www.wbotelhos.com.br/2010/12/06/manipulando-listas-com-jquery-e-vraptor-3/

Pelo que falei com o autor, ele explicou que só funciona com array de String e não com array de long, int etc.

L

talvez seja um problema com primitivos… se usar Long[] pode ser que resolva…

quer abrir um bug para ver isso?

S

E uma feature que há muito desejo é não precisar passar os índices na hora de submeter o array. Tanto a primeira sugestão do Daniel quanto a do Bruno (igual PHP) serviriam. Ter que calcular esses índices no JQuery ao fazer um form dinâmico é um suplício…

L

name=“qqerCoisa[]” já funciona, Sergio… o VRaptor substitui pelos índices.

S

Muito bom! Entendi aí de cima que não tava pronto ainda…

L

talvez tenha algum bug qto a primitivos…

D

Long[ ] id
ognl.OgnlException: id [br.com.caelum.vraptor.VRaptorException: Unable to find converter for [Ljava.lang.Long;]

long[ ] id
ognl.OgnlException: id [br.com.caelum.vraptor.VRaptorException: Unable to find converter for [J]

D

<input name="id[]" ..>

HTTP Status 500 -

type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception

java.lang.ArrayIndexOutOfBoundsException
	java.lang.reflect.Array.set(Native Method)
	ognl.ArrayPropertyAccessor.setProperty(ArrayPropertyAccessor.java:121)
	ognl.OgnlRuntime.setProperty(OgnlRuntime.java:2225)
	ognl.ASTProperty.setValueBody(ASTProperty.java:127)
	ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
	ognl.SimpleNode.setValue(SimpleNode.java:279)
	ognl.ASTChain.setValueBody(ASTChain.java:227)
	ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
	ognl.SimpleNode.setValue(SimpleNode.java:279)
	ognl.Ognl.setValue(Ognl.java:737)
	ognl.Ognl.setValue(Ognl.java:783)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createViaOgnl(OgnlParametersProvider.java:132)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createRoot(OgnlParametersProvider.java:108)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.getParametersFor(OgnlParametersProvider.java:90)
	br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.getParametersFor(ParametersInstantiatorInterceptor.java:108)
	br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:78)
	br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:42)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:44)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:81)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:70)
	br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
	br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:56)
	br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)
D

Único caso que funcionou:

public void delete(List&lt;Long&gt; id) { ... }

&lt;input name="id[0]" ...&gt; &lt;input name="id[1]" ...&gt; &lt;input name="id[2]" ...&gt;

L

esse caso não funciona?

public void delete(Long[] id) { ... }
<input name="id[0]" ...>  
<input name="id[1]" ...>  
<input name="id[2]" ...>
D

FUNCIONA!

public void delete(Long[] id) { ... }

&lt;input name="id[0]" ...&gt; &lt;input name="id[1]" ...&gt; &lt;input name="id[2]" ...&gt;

Isto aqui também funciona:

public void delete(String[] id) { ... }

&lt;input name="id" ...&gt; &lt;input name="id" ...&gt; &lt;input name="id" ...&gt;

Mas isto dá pau:

public void delete(String[] id) { ... }

&lt;input name="id[0]" ...&gt; &lt;input name="id[1]" ...&gt; &lt;input name="id[2]" ...&gt;

br.com.caelum.vraptor.VRaptorException: Unable to find converter for java.lang.String
D

Não seria uma boa ter suporta a lista e array sem precisar setar o índice? Independente do seu tipo (primitivo, wrapper, String)?
Entendo que quando for outro tipo de objeto, ai sim o índice é necessário.

L

danieldestro, esse caso do String[] foi corrigido, vc está com a última versão do VRaptor? a 3.2.0?

dependendo da sua versão até aquele ArrayIndexOutOfBounds já estaria corrigido.

qto a suportar sem setar o índice, isso já funciona na última versão usando o nome[]

D

vraptor-3.2.0
Sugestão: porque não suportar apenas “nome” ao invés de “nome[]” ?
Já que é fácil inferir se um valor único ou “array”.

L

pq não é o VRaptor que faz o trabalho sujo de popular os parâmetros, é o OGNL, e ele não suporta direito arrays sem índices. Ou seja, não vai ser tão fácil assim fazer a inferência.

se o Long[] for parâmetro direto do método até dá pra fazer sem problemas, mas e se ele estiver dentro de outro objeto?

por exemplo cliente.idsDasContas, ou conta.cliente.telefones. É bem difícil criar uma solução genérica, por isso a gente optou por usar o [] para indicar a array. Agora precisa investigar o bug de ArrayIndexOutOfBounds, e o de não suportar long[] e String[]

cria uma issue pra isso por favor? pode linkar para esse tópico

D

Feito: https://github.com/caelum/vraptor/issues/issue/315

Obrigado pelo suporte e ótimo trabalho com o vRaptor. Estou fazendo uma POC e vou usá-lo em um projeto meu.

L

obrigado vc pelas sugestões e pelo bug report =)

qqer dúvida é só falar

W

Lucas Cavalcanti:
talvez seja um problema com primitivos… se usar Long[] pode ser que resolva…

quer abrir um bug para ver isso?
http://github.com/caelum/vraptor/issues

Lucas, mesmo com objeto não funciona.

Só aproveitando a thread, uma feature que ajudaria muito seria aceitar ENUM como argumento.
É normal listar ENUMs nas views representando algo com perfil, por exemplo, e submetê-los de volta, seja um ou uma lista.
Fica muito trabalhoso rodar um for no controller para recuperar o ENUM correspondente a partir da String aceita como argumento.

Uma outra coisa seria o VRaptor olhar o tipo do argumento e não o que esta vindo como String, pois assim quando esperarmos como argumento String[] valores e vier apenas uma String ele colocaria na primeira posição do vetor em vez de dar erro de conversão de String para Array.
Colocar campos extras hidden para evitar esse erro e ser sempre um array é outra coisinha chata.

Tentei achar isso no código do VRaptor para tentar codar, mas não achei.

L

Washington, o VRaptor já aceita enums como argumento desde a primeira versão, como você tentou usar?

eu lembro de ter corrigido esse problema de passar só um elemento pro array, mas vou verificar se ainda existe esse problema. A implementação está em OgnlParametersProvider, e nas classes do mesmo pacote.
Não é uma coisa muito fácil de mudar, por isso a gente pensa em substituir o Ognl.

[]'s

W

Então Lucas,

Olhei um código antigo meu aqui e o problema era com uma lista de ENUM e não um valor apenas, eu que vacilei mesmo.
Como não consegui usar uma lista eu passo um String[] e depois faço um for para recuperar cada ENUM.

Isso é possível?:public void salvar(List<TipoPerfil> perfis) { }Erro:java.lang.IllegalArgumentException: Unable to identify field java.lang.String[] of type sun.reflect.generics.reflectiveObjects.GenericArrayTypeImplMe lembrei que você tinha consertado mesmo, então não é mais feito String -> String[].
Porém ainda sim é necessário pelo menos um campo hidden, pois se o usuário escolher apenas uma opção o array fica nulo em vez de colocar na primeira posição.

Tem como jogar na primera posição do array?

L

acabei de testar e List funciona, tranquilamente, tanto com indice, com [] e sem nada. Array de enums funcionou também

com String[] só dá o erro do unable to find converter for string, se vc usa indices. não consegui reproduzir o seu erro do IllegalArgument, qual é o código que dá o erro?
do jeito que eu testei não precisa nem do campo hidden

W

Oi Lucas,

Usando a versão 3.2 fiz os testes em um projeto em branco para evitar erros externos com os seguintes métodos:
@Post
@Path("/index/array")
public void metodoArray(String[] strings) {
	result.redirectTo(this).index();
}

@Post
@Path("/index/enum")
public void metodoEnum(TipoPerfil enumm) {
	result.redirectTo(this).index();
}

@Post
@Path("/index/enumz")
public void metodoEnum(TipoPerfil[] enumz) {
	result.redirectTo(this).index();
}

@Post
@Path("/index/enums")
public void metodoEnum(List<TipoPerfil> enums) {
	result.redirectTo(this).index();
}
Enviar apenas um valor para o método que espera um array de String o deixa nulo:
<input type="text" name="strings" value="valor"/>
Se colocar índice no name, é lançado a seguiente exception:
<input type="text" name="strings[0]" value="valor"/>
br.com.caelum.vraptor.VRaptorException: Unable to find converter for java.lang.String
	br.com.caelum.vraptor.core.DefaultConverters.to(DefaultConverters.java:59)
	br.com.caelum.vraptor.http.ognl.VRaptorConvertersAdapter.convert(VRaptorConvertersAdapter.java:60)
	br.com.caelum.vraptor.http.ognl.VRaptorConvertersAdapter.convertValue(VRaptorConvertersAdapter.java:56)
	ognl.ArrayPropertyAccessor.setProperty(ArrayPropertyAccessor.java:112)
	br.com.caelum.vraptor.http.ognl.ArrayAccessor.setProperty(ArrayAccessor.java:78)
	ognl.OgnlRuntime.setProperty(OgnlRuntime.java:2225)
	ognl.ASTProperty.setValueBody(ASTProperty.java:127)
	ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
	ognl.SimpleNode.setValue(SimpleNode.java:279)
	ognl.ASTChain.setValueBody(ASTChain.java:227)
	ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
	ognl.SimpleNode.setValue(SimpleNode.java:279)
	ognl.Ognl.setValue(Ognl.java:737)
	ognl.Ognl.setValue(Ognl.java:783)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createViaOgnl(OgnlParametersProvider.java:132)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createRoot(OgnlParametersProvider.java:108)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.getParametersFor(OgnlParametersProvider.java:90)
	br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.getParametersFor(ParametersInstantiatorInterceptor.java:108)
	br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:78)
	br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:42)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:44)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:81)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:70)
	br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
	br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:56)
	br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)
Quanto ao ENUM acho que o meu problema foi não usar índice, pois testando vi que só funciona com ele. Para um array de ENUM, basta que ao menos um campo tenha ou um abre ou fecha colchetes. Acho que isso já satisfaz o regex do VRaptor.
<input type="text" name="enumz" value="ADMINISTRADOR"/>
<input type="text" name="enumz]" value="ADMINISTRADOR"/>
Quanto a lista de ENUM basta que um campo tenha abre e fecha colchetes. Não podendo ser apenas um deles.
<input type="text" name="enumz" value="ADMINISTRADOR"/>
<input type="text" name="enumz[]" value="ADMINISTRADOR"/>
L

obrigado pelos casos de teste! =)
vou corrigir esses bugs asap então
Abraços

L

Washington e Daniel, testem por favor com esse snapshot do VRaptor?

https://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.2.1-SNAPSHOT/vraptor-3.2.1-20110117.201244-10.jar

só não vai funcionar array de primitivos, o resto deveria funcionar sem problemas

W

Fala Lucas,

Fiz todos os testes e agora esta tudo ok.

Bom trabalho, parabéns! (:

D

Fiz um teste com o vraptor-3.2.1-20110117.201244-10.jar.

&lt;input type="text" name="id" ... /&gt;

Funciona com qualquer uma das opções de método:

public void delete(String[] id) {} 

public void delete(long[] id) {} 

public void delete(Long[] id) {} 

public void delete(List&lt;Long&gt; id) {}

Bom trabalho!

Criado 24 de dezembro de 2010
Ultima resposta 27 de jan. de 2011
Respostas 32
Participantes 5