Vraptor + Angular + JSON

Pessoal,

Estou fazendo uma aplicação com Vraptor 3.5.0 + Angular e em determinado momento preciso enviar um JSON para o controller. Até aí tudo bem quando o JSON é simples o Vraptor consegue entender normalmente, porém quando o JSON é mais complexo com listas dentro de listas, o objeto das listas (listPeriod) sempre chega nulo ao controller, segue exemplo:

Com esse objeto o Vraptor popula normalmente

var teste = angular.toJson({request : 	{clientCode: 'xxx'})

Com um objeto desse tipo já não rola:

var teste = angular.toJson({
						request : 	{
								clientCode: 'XXXX',
								listPeriod: [{
									             	tariff : 	{
							             					from: '10/09/2013', 
												        to: '15/09/2013'
													} 
										}]
								}
						});

Na parte Java o código está assim:

@Path("/mockSearch")
@Consumes("application/json")
public void teste(HotelPriceRequest request) throws IOException {
      //IMPLEMENTACAO
}

Alguma idéia?

Muito Obrigado desde já…

os nomes dos campos estão corretos?

vc está usando o XStream ou o GSon?

Oi Lucas,

Sim os nomes estão certinhos, inclusive chega o primeiro parâmetro certo request.clientCode e além disso se eu passar esses campos como queryString chega certinho no controller.

Eu estou com as libs do XStream e do Gson no projeto, porém não sei qual é o padrão do Vraptor para desserializar JSON…

Pelo que andei lendo parece que o Gson é o mais indicado para esse tipo de caso, é isso mesmo? Se for isso, como fazer para que o VRaptor use o GSON? Preciso implementar algo ?

Abs e desde já muito obrigado pela ajuda…

para usar o GSON vc precisa colocar pacotes no web.xml…

br.com.caelum.vraptor.serialization.gson e br.com.caelum.vraptor.deserialization.gson

(no parametro de packages do VRaptor)

tenta usá-los e ver se funciona.

Oi Lucas,

Resolvi o problema…

Atualizei para a lib mais nova do VRaptor 3.5.2 (Snapshot) e registrei o gson no web.xml. Ao fazer isso as listas chegaram preenchidas corretamente no controller…

Muito Obrigado pela ajudaaaaa
Lucas

Vi que a troca da versão e o registro no web.xml dos packages gerou um efeito colateral indesejado…

A deserialização do json passou a funcionar normalmente através do GSON, porém quando vou serializar, tenho tomado o seguinte erro

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘gsonDeserialization’: Unsatisfied dependency expressed through constructor argument with index 1 of type [br.com.caelum.vraptor.deserialization.gson.JsonDeserializers]: : Error creating bean with name ‘defaultJsonDeserializers’: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.util.List]: : No matching bean of type [com.google.gson.JsonDeserializer] found for dependency [collection of com.google.gson.JsonDeserializer]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.google.gson.JsonDeserializer] found for dependency [collection of com.google.gson.JsonDeserializer]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘defaultJsonDeserializers’: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.util.List]: : No matching bean of type [com.google.gson.JsonDeserializer] found for dependency [collection of com.google.gson.JsonDeserializer]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.google.gson.JsonDeserializer] found for dependency [collection of com.google.gson.JsonDeserializer]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

O interessante é que antes dessas mudanças o que acontecia era exatamente o contrário, ou seja, conseguia serializar mas não conseguia deserializar

	<context-param>  
	    <param-name>br.com.caelum.vraptor.packages</param-name>  
	    <param-value>br.com.caelum.vraptor.util.jpa,  
	       br.com.caelum.vraptor.serialization.gson,  
	       br.com.caelum.vraptor.deserialization.gson  
	    </param-value>  
	</context-param>

Alguma ideia?

Muito Obrigado,
Lucas

pra isso é preciso usar o VRaptor 3.5.2-SNAPSHOT…

a versao final do 3.5.2 vai sair em breve

https://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.5.2-SNAPSHOT/

Oi Lucas,

Eu já estava usando essa versão 3.5.2 com a versão de 14 de agosto e o problema ainda acontece…

Tem mais alguma ideia?

Agradeço bastante…

Abs,
Lucas

Muito estranho… ele tá reclamando da falta de uma dependência que existe:

será que vc não está com mais de um jar do VRaptor no classpath?

já tentou dar um clean no projeto e no servidor?

=)

Oi Lucas,

Só estou com essa versão no classpath: vraptor-3.5.2-20130814.144000-8

E não tinha feito o clean no projeto e no server, mas já fiz e infelizmente não resolveu o problema não…

É esse o erro:

br.com.caelum.vraptor.InterceptionException: exception raised, check root cause for details: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘gsonJSONSerialization’: Unsatisfied dependency expressed through constructor argument with index 3 of type [br.com.caelum.vraptor.serialization.gson.VRaptorGsonBuilder]: : Error creating bean with name ‘VRaptorGsonBuilder’: Unsatisfied dependency expressed through constructor argument with index 1 of type [br.com.caelum.vraptor.serialization.xstream.Serializee]: : No matching bean of type [br.com.caelum.vraptor.serialization.xstream.Serializee] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [br.com.caelum.vraptor.serialization.xstream.Serializee] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘VRaptorGsonBuilder’: Unsatisfied dependency expressed through constructor argument with index 1 of type [br.com.caelum.vraptor.serialization.xstream.Serializee]: : No matching bean of type [br.com.caelum.vraptor.serialization.xstream.Serializee] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [br.com.caelum.vraptor.serialization.xstream.Serializee] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

Abs,
Lucas

Gerei outro snapshot, tenta lá de novo, por favor:

https://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.5.2-SNAPSHOT/vraptor-3.5.2-20130815.171520-9.jar

Oi Lucas,

Testei aqui e funcionou perfeitamente…

Percebi um outro bug que acho que é do GSON… Um dos meus parâmetros se chama “from” e o outro “to”, parâmetros com esses nomes, a deserialização usando o GSON gera o erro abaixo

br.com.caelum.vraptor.view.ResultException: Unable to deserialize data
at br.com.caelum.vraptor.deserialization.gson.GsonDeserialization.deserialize(GsonDeserialization.java:97)
at br.com.caelum.vraptor.interceptor.DeserializingInterceptor.intercept(DeserializingInterceptor.java:87)
at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)

Trocando simplesmente o nome do parâmetro, o objeto chega certinho no controller… É isso mesmo, esses nomes são reservados? Ou devo fazer alterar alguma coisa…

Abs,
Lucas

estranho… tem algum caused by da stacktrace que explica melhor o erro?

Oi Lucas,

Caused by não tem… segue ela completa…

br.com.caelum.vraptor.view.ResultException: Unable to deserialize data
	at br.com.caelum.vraptor.deserialization.gson.GsonDeserialization.deserialize(GsonDeserialization.java:97)
	at br.com.caelum.vraptor.interceptor.DeserializingInterceptor.intercept(DeserializingInterceptor.java:87)
	at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
	at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:48)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
	at br.com.caelum.vraptor.interceptor.ExceptionHandlerInterceptor.intercept(ExceptionHandlerInterceptor.java:67)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
	at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:96)
	at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
	at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:83)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
	at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
	at br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:44)
	at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91)
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58)
	at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1212)
	at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:399)
	at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
	at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
	at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
	at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:450)
	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
	at org.mortbay.jetty.Server.handle(Server.java:326)
	at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
	at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:945)
	at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
	at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
	at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
	at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
	at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

Vou ver se consigo alguma coisa para escapar (esses parâmetros com esses nomes) ou no pior caso vou mudar o nome do parâmetro…

Abs,
Lucas

Oi Lucas,

Na verdade vi aqui que o problema é que esses 2 campos são do tipo Date e por serem Date o Vraptor com o Gson não estão conseguindo interpretar…

O formato que estou enviando é dd/MM/yyyy. Tenho até um Converter implementado mas quando chega no Converter o value está nulo, ou seja, me parece que primeiramente o Vraptor passa pela deserialização do Gson que não entende a data e coloca null no valor e o meu converter nesse caso não funciona de nada…

Alguma idéia?

Abs,
Lucas

o Converter não vai funcionar para o JSON… vc precisa escrever um JsonDeserializer de date, que usa o seu formato de data:

@Component
public class DateConverter implements JsonDeserializer<Date> { .... }

Valeu Lucas… Funcionou legal !!!

Olá Lucas, desculpe reabrir este seu post, mas é que estou enfrentando o mesmo problema que você e não consigo encontrar uma solução adequada.

Gostaria que pudesse me explicar melhor sobre dois aspectos em seu código de exemplo:

O primeiro, é sobre o teu JSON montado via angular:

var teste = angular.toJson({  
                        request :   {  
                                clientCode: 'XXXX',  
                                listPeriod: [{  
                                                    tariff :    {  
                                                            from: '10/09/2013',   
                                                        to: '15/09/2013'  
                                                    }   
                                        }]  
                                }  
                        });  

Eu utilizo algo similar, mas faço o trabalho de transmissão via jQuery, conforme exemplo que se segue:

var data =  {
	    cmd     : 'save-record',
	    name    : 'form',
	    recid   : 10,
	    record  : {
	    	field1 : 'value1',
	    	fieldN : 'valueN'
		}
	};

$.ajax({
	  type: "POST",
	  url: "system/saveRecord",
	  data: data,
	  success: function(json) {console.log('Concluído: ' + json)},
	  dataType: "json"
	});

Pois bem, estes dados são enviados ao servidor e acionam meu método saveRecord.

Eu gostaria de saber como vc envia seu código para o server?

Aí entramos na segunda questão, o server side.

Em seu código vc nos mostrou o seguinte:

@Path("/mockSearch")  
@Consumes("application/json")  
public void teste(HotelPriceRequest request) throws IOException {  
      //IMPLEMENTACAO  
}  

A minha dúvida é em relação à classe HotelPriceRequest, ela implementa ou estende algo?

E depois disto, como é utilizado o GSON? Como funciona a deserialização de teu objeto?

No exemplo de JSON que passei acima eu consigo os valores de cmd, name e recid. O bicho pega quando tento recuperar o que está na lista de record.

Agradeço qualquer ajuda!

o código do $.ajax que vc mandou não vai enviar um JSON, e sim form parameters… daí vc pode usar o jeito normal de popular os parâmetros, sem o @Consumes.

Mas daí o json teria que ter os atributos mais ou menos assim:

{
   "request.cmd" : "save-record",
   "request.name": "form",
    ....
}