Vraptor 3 e JSON

15 respostas
B

Pessoal to tentando usar o esquema da implementação de json na versão 3 só que ta me retornando um json estranho será bug??

vo postar os códigos

@Path("/audiometro/ultimaafericao/{audiometro.id}")
	   public void ultimaafericao(Audiometro audiometro)
	   {
		   result.use(Results.json()).from(dao.recupera(audiometro.getId())).serialize();
	   }

esta retornando o seguinte json

{“audiometro_$$_javassist_5”: {

"@resolves-to": org.hibernate.proxy.pojo.javassist.SerializableProxy,

entityName”: br.com.ddns.fonaudio.models.Audiometro,

persistentClass”: br.com.ddns.fonaudio.models.Audiometro,

interfaces”: [

“org.hibernate.proxy.HibernateProxy”

],

id”: {

"@class": long,

"$": 1

}

}}

enquanto era pra retornar isso num era??

{“audiometro”: {

“id”: “1”,“modelo”:“CDA3000”

}}

to comendo bola em algum ponto??

15 Respostas

J

Poste sua classe aqui toda !

documentação !

B

ja tinha lido toda a documentação antes de postar e la ta falando exatamente o que estou questionando a documentação falou que era pra renderizar o json de um modo
so que ta renderizando de outro modo…

classe Audiometro

package br.com.ddns.fonaudio.models;


import java.util.Date;

import javax.persistence.Column;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;


@Entity
@Table(name="fax_audiometros")
@NamedQuery (name="audiometrosAtivos",query="select new br.com.ddns.fonaudio.models.Audiometro(c.id,c.modelo,c.ultimaafericao) FROM Audiometro as c WHERE c.ativo = :ativo and c.clinica.id= :id")
public class Audiometro {
	@Id
    @GeneratedValue
    @Column(name="aud_id")
	private Long id;
	@Column(name="aud_modelo")
	private String modelo;
	@Column(name="aud_ativo")
	private char ativo='S';
	@Temporal(TemporalType.DATE)
	@Column(name="aud_ultimaafericao")
	private Date ultimaafericao;
	
	@OneToOne
	@JoinColumn(name="cli_id")
	private  Clinica clinica;
	public Audiometro() {}
	public Audiometro(Long id,String modelo,Date ultimaafericao) {
		this.id=id;
		this.modelo=modelo;
		this.ultimaafericao=ultimaafericao;
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getModelo() {
		return modelo;
	}
	public void setModelo(String modelo) {
		this.modelo = modelo;
	}
	public char getAtivo() {
		return ativo;
	}
	public void setAtivo(char ativo) {
		this.ativo = ativo;
	}
	public void setClinica(Clinica clinica) {
		this.clinica = clinica;
	}
	public Clinica getClinica() {
		return clinica;
	}
	public void setUltimaafericao(Date ultimaafericao) {
		this.ultimaafericao = ultimaafericao;
	}
	public Date getUltimaafericao() {
		return ultimaafericao;
	}
	
	
}

Meu controlador

package br.com.ddns.fonaudio.controllers;

import java.util.List;

import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.view.Results;
import br.com.ddns.fonaudio.MySession;
import br.com.ddns.fonaudio.annotations.VerifyAccess;
import br.com.ddns.fonaudio.dao.AudiometroDao;
import br.com.ddns.fonaudio.dao.EmpresaDao;
import br.com.ddns.fonaudio.models.Audiometro;
import br.com.ddns.fonaudio.models.Clinica;
import br.com.ddns.fonaudio.models.Empresa;
import br.com.ddns.fonaudio.models.Tipoexame;
import br.com.ddns.fonaudio.models.Usuario;

@Resource
public class AudiometroController {
	   private AudiometroDao dao;
	   private Result result;
	   private MySession sessao;
	   public AudiometroController(MySession sessao,AudiometroDao dao,Result result) {
	        this.dao = dao;
	        this.result=result;
	        this.sessao=sessao;
	    }
	   @VerifyAccess(action=23)
	   public List<Audiometro> lista() {
	       return dao.listaTodos();
	   }
	   @VerifyAccess(action=24)
	   public void form() {
		   Audiometro audiometro=new Audiometro();
		   audiometro.setClinica(((Usuario)sessao.getObjectSession("usuario")).getClinica());
		   result.include("audiometro",audiometro);
	   }
	   
	   @Path("/audiometro/ultimaafericao/{audiometro.id}")
	   public void ultimaafericao(Audiometro audiometro)
	   {
		  result.use(Results.json()).from(dao.recupera(audiometro.getId())).serialize();
	   }
	   public void mensagem() {
	   }
	   
	   @Post
	   public void adiciona(Audiometro audiometro) {
		    if(audiometro.getId()==null) 
		    {
		    	result.include("insert", true);
		    	dao.adiciona(audiometro);
		    }
		   	else 
		   	{
		   		result.include("insert", false);
		   		dao.atualiza(audiometro);
		   	}
		    result.use(Results.logic()).redirectTo(AudiometroController.class).mensagem();
	   }
	   
	   @VerifyAccess(action=25)
	   @Path("/audiometro/edit/{audiometro.id}")
	   public void edita(Audiometro audiometro){
	   	result.include("audiometro",dao.recupera(audiometro.getId()));
	   	result.use(Results.page()).forward("/WEB-INF/jsp/audiometro/form.jsp");
	   }
	   
	   @VerifyAccess(action=26)
	   @Path("/audiometro/delete/{ids}")
	   public void delete(String ids){
	   	for(String id : ids.split(",")) dao.desativa(dao.recupera(new Long(id)));
	   	result.use(Results.nothing());
	   }
}

esta retornando o seguinte json

{“audiometro_$$_javassist_5”: {

"@resolves-to": org.hibernate.proxy.pojo.javassist.SerializableProxy,

entityName”: br.com.ddns.fonaudio.models.Audiometro,

persistentClass”: br.com.ddns.fonaudio.models.Audiometro,

interfaces”: [

“org.hibernate.proxy.HibernateProxy”

],

id”: {

"@class": long,

"$": 1

}

}}

enquanto era pra retornar isso

{“audiometro”: {

“id”: “1”,“modelo”:“CDA3000”

}}
P

Oi Boneazul!

primeiro, pra que saiua audiometro como raiz, voce passa a string como segundo argumento:

result.use(Results.json()).from(dao.recupera(audiometro.getId()), “audiometro”).serialize();

pra retirar os atributos que voce nao quer que aparecam (que sao da proxy gerada pelo javassist, por causa do uso do hibernate), voce pode invocar o metodo excludes em relacao a eles

abracos

G

Sua classe está correta, não há nada de errado com ela. E nem deveria haver.

Isso que está acontecendo é um comportamento do xstream X javassist. Quando você faz uma pesquisa via hibernate ele te retorna um proxy ao invés de uma entidade real. Esse proxy é apenas para te adicionar funcionalidades como lazy-load e afins. Mas sua classe continua com os mesmos métodos e tudo mais. Apenas é um “falso objeto”.

Quando o xstream serializa o objeto ele não sabe disso e serializa o teu proxy. Não conheço a fundo o xstream, mas deve haver algum workaround para isso. Você pode fazer algumas pesquisas sobre javassist xstream serialization.

Uma solução que ví em um projeto que dei consultoria foi de usar converters, mas isso tornase tão trabalhoso quando criar um DTO.

Paulo, a solução que você propôe não funcionaria, pois o elemento root dele é audiometro_$$_javassist_5, e esse valor muda a cada leitura, ficando quase impossível fazer um exclude.

As opções que te dou é ou usar um objeto que você coloque apenas as propriedades que você quer serializar e da forma que você quer exibir (basicamente um DTO) e antes de chamar o json do vraptor você faça a transformação.

Outra opção é você tentar usar o método get ao invés de load, pois get retorna sem lazy-load, creio que vá te retornar sem o proxy do JA.

Abraços

B

Paulo Silveira:
Oi Boneazul!

primeiro, pra que saiua audiometro como raiz, voce passa a string como segundo argumento:

result.use(Results.json()).from(dao.recupera(audiometro.getId()), “audiometro”).serialize();

pra retirar os atributos que voce nao quer que aparecam (que sao da proxy gerada pelo javassist, por causa do uso do hibernate), voce pode invocar o metodo excludes em relacao a eles

abracos

Paulo estou usando a versão 3.0.2 e não existe uma assinatura no metodo from que aceite um (T arg,String arg1)
como voce descreveu acima from(dao.recupera(audiometro.getId()), “audiometro”)…

apenas assinatura com (T arg) from(dao.recupera(audiometro.getId())) que eu estou usando …

ja tem outra versão release depois da 3.0.2?

G

boneazul:
Paulo estou usando a versão 3.0.2 e não existe uma assinatura no metodo from que aceite um (T arg,String arg1)
como voce descreveu acima from(dao.recupera(audiometro.getId()), “audiometro”)…

apenas assinatura com (T arg) from(dao.recupera(audiometro.getId())) que eu estou usando …

ja tem outra versão release depois da 3.0.2?

O que o Paulo quis dizer é:

Audiometro obj = dao.recupera(audiometro.getId()); result.use(Results.json()).from(obj, "audiometro").exclude("xxxxx").serialize();

B

garcia-jj:
Sua classe está correta, não há nada de errado com ela. E nem deveria haver.

Isso que está acontecendo é um comportamento do xstream X javassist. Quando você faz uma pesquisa via hibernate ele te retorna um proxy ao invés de uma entidade real. Esse proxy é apenas para te adicionar funcionalidades como lazy-load e afins. Mas sua classe continua com os mesmos métodos e tudo mais. Apenas é um “falso objeto”.

Quando o xstream serializa o objeto ele não sabe disso e serializa o teu proxy. Não conheço a fundo o xstream, mas deve haver algum workaround para isso. Você pode fazer algumas pesquisas sobre javassist xstream serialization.

Uma solução que ví em um projeto que dei consultoria foi de usar converters, mas isso tornase tão trabalhoso quando criar um DTO.

Paulo, a solução que você propôe não funcionaria, pois o elemento root dele é audiometro_$$_javassist_5, e esse valor muda a cada leitura, ficando quase impossível fazer um exclude.

As opções que te dou é ou usar um objeto que você coloque apenas as propriedades que você quer serializar e da forma que você quer exibir (basicamente um DTO) e antes de chamar o json do vraptor você faça a transformação.

Outra opção é você tentar usar o método get ao invés de load, pois get retorna sem lazy-load, creio que vá te retornar sem o proxy do JA.

Abraços

Como estava com pressa acabei resolvendo de um modo menos programática, colocando o objeto na view e montando o json na mão mesmo.
Essa solução eu ate já até conhecia é que nesse caso em particular é apenas um atributo de um objeto que preciso acessar mas se fosse uma necessidade de mais atributos como uma instancia de banco inteira com os lazy ou uma coleção deles ficaria bem trabalhoso a montagem

queria utilizar da forma programática que acredito ser bem mais limpa ,enxuta e rápida…

Quanto as opções acho ainda mais facil renderizar na view usando o formato logica.json.jsp
É que uso muito ajax na aplicação ai conforme cresce os requisitos o sistema começa a usar muito esse tipo de recurso que é bacana de ter.Acho que o DTO seria mais trabalhoso e quanto a usar o get não fiz o teste pra saber como seria o retorno mas como meu GenericDao usa load pra tudo trocar pra get acho que quebraria outros pontos teria que testar.

B

garcia-jj:
boneazul:
Paulo estou usando a versão 3.0.2 e não existe uma assinatura no metodo from que aceite um (T arg,String arg1)
como voce descreveu acima from(dao.recupera(audiometro.getId()), “audiometro”)…

apenas assinatura com (T arg) from(dao.recupera(audiometro.getId())) que eu estou usando …

ja tem outra versão release depois da 3.0.2?

O que o Paulo quis dizer é:

Audiometro obj = dao.recupera(audiometro.getId()); result.use(Results.json()).from(obj, "audiometro").exclude("xxxxx").serialize();

entao não existe assinatura from(T obj,String arg) apenas from(T obj)

result.use(Results.json()).from(obj, audiometro).exclude(xxxxx).serialize();

//erro de compilação

//The method from(T) in the type JSONSerialization is not applicable for the arguments (Audiometro, String)

entendeu??

J

O Paulo Silveira falou tudo…

G

boneazul, dê uma olhada no github, esse método está lá. http://github.com/caelum/vraptor/blob/master/vraptor-core/src/main/java/br/com/caelum/vraptor/serialization/XStreamJSONSerialization.java . Não sei se esse método é novo, mas você pode optar por compilar do github caso isso não tenha na sua versão.

Mesmo assim dúvido que funcione, pois assim você está apenas criando um alias para o root element e nada mais. O retorno será o mesmo. No seu caso creio que a única forma são as opções que citei acima.

Quando a usar um JSP ou um DTO você tem praticamente o mesmo trabalho. É uma classe Java ao invés de um JSP. Eu ainda não havia passado por isso porque os projetos que usei vraptor eram distribuidos, daí o uso normal do DTO. Creio que no seu caso uma classe que contivesse a estrutura do seu JSON seria mais interessante, assim você usa o result automatico do vraptor.

Junior, na verdade o que o Paulo falou não funciona, e eu já havia comentado acima e complementei nesse post. A menos que eu não tenha entendido o que você quis dizer. Mas explico meu ponto de vista:

Isso apenas cria um alias, mas não resolve o problema de continuar retornando o proxy do javassist.

Esse não funciona porque o objeto que ele quer está dentro do javassist. Assim se ele excluir as informações do javassist ele removerá também seu objeto retornando um json vazio.

J

garcia-jj Boa Tarde !

Testei aqui… retorna vázio ! Estranho !

G

juniorsatanas:
garcia-jj Boa Tarde !

Testei aqui… retorna vázio ! Estranho !

Foi isso mesmo que eu havia falando. Olhe o resultado original. Se você fizer um exclude do objeto do javassist você exclui tudo, pois ele é o seu objeto root. A entidade Audiometro que ele quer está dentro do proxy do javassist, assim se ele colocar o javassist no exclude TUDO será excluído, por isso o retorno em branco.

{ "audiometro_$$_javassist_5": { "@resolves-to": "org.hibernate.proxy.pojo.javassist.SerializableProxy", "entityName": "br.com.ddns.fonaudio.models.Audiometro", "persistentClass": "br.com.ddns.fonaudio.models.Audiometro", "interfaces": [ "org.hibernate.proxy.HibernateProxy" ], "id": { "@class": "long", "$": "1" } } }

Lembrando que proxies do javassist e cglib são gerados dinamicamente. Nesse caso temos aqui um audiometro_$$javassist_5, mas na próxima geração pode ser um audiometro$$javassist_90 e até um audiometro$$_javassist_77. Como saber qual o nome do root-element? Somente usando o alias, mas mesmo assim nada ajuda ao problema inicial do tópico.

Proxies dinamicos implementam em tempo de execução classes dinamicas que implementam a classe real, nesse caso a Audiometro. Como o xstream faz reflexão nos atributos da classe dá essa xica, pois ele faz a reflexão nessa classe gerada dinamicamente.

P

oi boneazul!

tem razao, é um metodo da versao 3.10 que esta pra sair em menos de duas semanas. tem bastante novidades.

criei ate um issue pro seu caso, pois vai ser comum querer serializar entidades proxiadas do hibernate:

o que acha?
http://github.com/caelum/vraptor/issues/labels/3.1#issue/174

G

Paulo Silveira:
oi boneazul!

tem razao, é um metodo da versao 3.10 que esta pra sair em menos de duas semanas. tem bastante novidades.

criei ate um issue pro seu caso, pois vai ser comum querer serializar entidades proxiadas do hibernate:

o que acha?
http://github.com/caelum/vraptor/issues/labels/3.1#issue/174

Paulo, será que sai aquele exception handler nessa versão?

Abraços

B

Paulo Silveira:
oi boneazul!
tem razao, é um metodo da versao 3.10 que esta pra sair em menos de duas semanas. tem bastante novidades.
criei ate um issue pro seu caso, pois vai ser comum querer serializar entidades proxiadas do hibernate:
o que acha?
http://github.com/caelum/vraptor/issues/labels/3.1#issue/174

por mim tá otimo…acabei resolvendo de outro modo como postado logo acima…
eu só postei mesmo porque tinha cara de issue o que tava acontecendo mesmo assim obrigado a todos pela ajuda…

Criado 27 de dezembro de 2009
Ultima resposta 28 de dez. de 2009
Respostas 15
Participantes 4