VRaptor + JSON

35 respostas
R

Boa tarde pessoal,

Estou com um problema meio estranho ao tentar consumir um JSON desta forma:

O método no controller:

@Consumes("application/json")
	public void salvarTipoApoio(TipoApoio tipoApoio) {
		tipoApoioDao.salvar(tipoApoio);
	}

A chamada Ajax:

var tipoApoio = {
			tipoApoio : {
				id : 22,
				nome : 'TESTE'
			}
	};
	
	alert(JSON.stringify(tipoApoio));
	
	$.ajax({
        type : 'POST',
        contentType : 'application/json',
        url : '<%= request.getContextPath() %>/service/salvarTipoApoio',
        data : JSON.stringify(tipoApoio),
        dataType: 'json',
        beforeSend : function() {
            alert('antes de enviar');
        },
        success : function(txt) {
            alert('envio sucesso');
        },
        error : function(txt) {
            alert('erro envio');
        }
    });

O erro é:

Caused by: java.lang.NullPointerException
	at br.com.caelum.vraptor.serialization.xstream.VRaptorClassMapper.shouldSerializeMember(VRaptorClassMapper.java:55)
	at com.thoughtworks.xstream.mapper.MapperWrapper.shouldSerializeMember(MapperWrapper.java:74)
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:301)
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:234)
	at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72)
	... 53 more

O JSON está sendo montado corretamente, desta forma:

{"tipoApoio":{"id":22,"nome":"TESTE"}}

Vi que outras pessoas tiveram o mesmo problema, porém não consegui resolver. Alguém poderia dar uma força?

35 Respostas

R

Mais código de erro para ajudar:

com.thoughtworks.xstream.converters.ConversionException: null : null
---- Debugging information ----
cause-exception     : java.lang.NullPointerException
cause-message       : null
class               : br.gov.ce.sejus.sestap.model.TipoApoio
required-type       : br.gov.ce.sejus.sestap.model.TipoApoio
converter-type      : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
line number         : -1
version             : null
-------------------------------
	at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:79)
A

sua classe TipoApoio tá compatível com esse json? Dá pra postar ela ai?

R

Opa alias, com certeza, segue:

@Entity
@Table(name = "tipo_apoio", schema = "public")
public class TipoApoio implements java.io.Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -3177706573195003152L;
	private Integer id;
	private String nome;

	public TipoApoio() {
	}

	public TipoApoio(Integer id, String nome) {
		this.id = id;
		this.nome = nome;
	}

	@Id
	@Column(name = "id", unique = true, nullable = false)
	public Integer getId() {
		return this.id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	@Column(name = "nome", nullable = false, length = 100)
	public String getNome() {
		return this.nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

}
L

qual versao do VRaptor?

R

Estou usando a 3.4.1 (vraptor-3.4.1.jar)

L

tenta usar o 3.5.0, eu tinha corrigido esse erro.

R

Atualizei, porém o erro continuou, aí criei uma classe que implementa br.com.caelum.vraptor.deserialization.Deserializer, e o método deserialize está sendo chamado. Está é a solução correta?

L

faz essa sua classe que implementa deserializer estender JsonDeserializer, e veja se funciona.

R

Lucas, a classe ficou assim:

import java.io.BufferedReader;  
import java.io.InputStream;  
import java.io.InputStreamReader;  
  
import br.com.caelum.vraptor.deserialization.Deserializer;  
import br.com.caelum.vraptor.deserialization.Deserializes;  
import br.com.caelum.vraptor.deserialization.JsonDeserializer;  
import br.com.caelum.vraptor.http.ParameterNameProvider;  
import br.com.caelum.vraptor.interceptor.TypeNameExtractor;  
import br.com.caelum.vraptor.resource.ResourceMethod;  
import br.com.caelum.vraptor.serialization.xstream.XStreamBuilder;  
import br.com.teste.model.TipoApoio;  
  
import com.google.gson.Gson;  
import com.google.gson.GsonBuilder;  
  
@Deserializes("application/json")  
public class Helper extends JsonDeserializer implements Deserializer {  
  
    public Helper(ParameterNameProvider provider, TypeNameExtractor extractor,  
            XStreamBuilder builder) {  
        super(provider, extractor, builder);  
    }  
  
    @Override  
    public Object[] deserialize(InputStream arg0, ResourceMethod arg1) {  
        Gson gson = new GsonBuilder().create();  
        BufferedReader reader = new BufferedReader(new InputStreamReader(arg0));  
        TipoApoio tipoApoio = gson.fromJson(reader, TipoApoio.class);  
        return null;  
    }  
  
}

Estou retornando null pois realmente não faço ideia do que devo retornar. De qualquer forma o objeto TipoApoio está vindo com id e nome nulos do fromJson...

L

se vc vai modificar toda a deserialização não precisa fazer o extends, só o implements Deserializer já é o suficiente.

talvez se vc tirar o {‘tipoApoio’: } em volta do resto do JSON funcione (ou seja, deixar sem raiz)

vc pode também tentar usar a integração do GSON que já existe no VRaptor 3.5.0

registre o pacote br.com.caelum.vraptor.deserialization.gson no web.xml e remova o @deserializes desse seu deserializer.

R

Lucas, fiz assim mas não chega mais no Helper, dando essa exception:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'gsonDeserialization': Unsatisfied dependency expressed through constructor argument with index 1 of type [java.util.Collection]: : 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: {} at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:730) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:196) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory$2.getObject(AbstractBeanFactory.java:332) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:43) [spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:328) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:274) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1106) [spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE] at br.com.caelum.vraptor.ioc.spring.SpringBasedContainer.instanceFor(SpringBasedContainer.java:86) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.deserialization.DefaultDeserializers.deserializerFor(DefaultDeserializers.java:46) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.interceptor.DeserializingInterceptor.intercept(DeserializingInterceptor.java:81) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.hydrustechnology.meubolao.util.Interceptador.intercept(Interceptador.java:39) [classes:] at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.interceptor.ExceptionHandlerInterceptor.intercept(ExceptionHandlerInterceptor.java:67) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:48) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:83) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:96) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:44) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58) [vraptor-3.5.0.jar:] at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88) [vraptor-3.5.0.jar:] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:] at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:] at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:] at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:] at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:] at java.lang.Thread.run(Unknown Source) [rt.jar:1.7.0_01] Caused by: 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: {} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:952) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:779) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:735) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:795) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:723) [spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] ... 55 more

L

registra esse pacote tb:
br.com.caelum.vraptor.serialization.gson.adapters

R

Lucas, fiz isso mas o erro persistiu.

L

estranho…

cria essa classe no seu projeto então:

R

Lucas, aconteceu uma coisa estranha. Quando fui testar novamente com esta alteração que vc disse, consegui chegar até meu método salvarTipoApoio, só que estava vindo null, então voltei a raiz do json como estava antes e funcionou.

Será que foi algum pacote que registramos? Vou testar aqui.

R

Estranho, tenho as classes CalendarDeserializer.java e minha Helper:

public class Helper implements Deserializer {

	@Override
	public Object[] deserialize(InputStream arg0, ResourceMethod arg1) {
		Gson gson = new GsonBuilder().create();
		BufferedReader reader = new BufferedReader(new InputStreamReader(arg0));
		TipoApoio tipoApoio = gson.fromJson(reader, TipoApoio.class);
		return null;
	}

}

Se eu tirar qualquer uma dessas 2 classes, dá erro.

L

qual é o erro que acontece quando vc tira o helper?

R

Lucas, confusão minha, a falta do Helper não causa erro algum, funciona apenas adicionando a classe que vc pediu.

Só não entendi a necessidade de classe CalendarDeserializer.

Lucas, agradeço mais uma vez pela valiosa ajuda!

L

é que precisa ter alguma implementação de JsonDeserializer registrada… esse CalendarDeserializer foi lançado no pacote errado, por isso não foi registrado.

D

Bom dia Lucas,

Estou com o mesmo problema, mas eu não posso migrar para a versão 3.5 agora devido a problemas com a função linkTo.
Seguindo as dicas eu registrei o pacote:

<context-param>
		<param-name>br.com.caelum.vraptor.packages</param-name>
		<param-value>
			br.com.caelum.vraptor.converter.l10n, 
			br.com.caelum.vraptor.serialization.gson.adapters
		</param-value>
	</context-param>

E criei o JsonDeserializer para Calendar, mas ainda não funcionou.
Fazendo um teste com a classe Helper criada pelo Ribker funcionou, entretanto ficou um deserializer estático para um tipo.

@Deserializes("application/json")
public class Helper implements Deserializer {

    @Override    
    public Object[] deserialize(InputStream arg0, ResourceMethod arg1) {    
        Gson gson = new GsonBuilder().create();
        BufferedReader reader = new BufferedReader(new InputStreamReader(arg0));    
        Tarefa tarefa = gson.fromJson(reader, Tarefa.class);
        return new Object[]{ tarefa };
    }   
}

Como eu posso resolver mantendo a versão 3.4.1?

L

a versão 3.5.1 corrige os problemas do linkTo, tenta fazer a migração =)

D

show!!!
dois coelhos com a mesma cajadada…

D

só mais uma pergunta Lucas:

Não é possível enviar dois objetos?

public void metodo(Objeto1 objeto1, Objeto2 objeto2) { ... }

Tentei e o segundo está vindo nulo.

L

é possível sim… só usar os nomes dos inputs a partir dos nomes dos parametros:

objeto1.propriedade1
objeto1.propriedade2

objeto2.propriedade1
objeto2.propriedade2

lembrando que é o nome do parametro que vale, não o nome da classe.

D

usando json do angularjs?

var json = angular.toJson({ 
			obj1: {...}, 
			obj2: {...} 
		});

o primeiro chega mas o segundo não

L

vc consegue ver quais parametros foram na requisição? tipo usando o firebug ou o developer tools?

D

estão indo os dois (vide anexo):

assinatura do metodo:

@Consumes("application/json")
	@Put("/s/{tarefa.id}")
	public void s(Tarefa tarefa, Processo processo)


L

o @Consumes de json e de xml padrão só suportam um objeto =(

tenta criar essa classe na sua aplicação:

@Component
public void MeuGSonDeserializer extends GsonDeserialization {
   //delegate constructor
}

creio que isso deva resolver…

D

Não deu certo mas eu criei um wrapper.

Acha necessário criar uma issue ou é muito específico?

L

cria a issue sim, por favor…

mas só funcionaria com json, em xml isso já não faria sentido (já que tem que ter uma raíz única)

D

Lucas,

Fora a questão da deserialização de mais de um parâmetro, também estou tendo problemas com deserialização de subobjetos.
Quando uma propriedade é um outro objeto composto é deserializado nulo.

Ex.:
Meu objeto tarefa tem uma propriedade grupoAtendente, ela está chegando nula no controller, somente os tipos primitivos e seus wrappers estão chegando preenchidos.

O que fazer para tentar solucionar ou descobrir onde está o problema?

L

isso usando o gson ou o XStream?

D

o deserializer do vraptor não é recursivo?

L

o do XStream é, se eu não me engano… o do GSon eu não cheguei a testar esse caso.

D

Lucas,

Na verdade eu estava registrando o pacote errado.
Analisando as classes de deserialização vi que a classe do Gson está no pacote br.com.caelum.vraptor.deserialization.gson.

Registrei ele e passou a funcionar tudo, multiplos parâmetros e classes complexas.

Mas é necessário registrar o pacote de adapters também, então registrei estes dois:

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

Criado 7 de maio de 2013
Ultima resposta 21 de mai. de 2013
Respostas 35
Participantes 4