[RESOLVIDO] Dúvidas VRaptor3 x JSON

25 respostas
smillodont

Olá pessoal,

conforme falado em uma outra dúvida que acabei de postar, estou iniciando com o VRaptor3 e estou com algumas dúvidas. No caso, sobre JSON. Seguem algumas perguntas, se alguém puder me ajudar, ficarei muito grato:

PERGUNTA 1: como gerar um JSON com apenas um elemento de tipo primitivo?

Exemplo: quero gerar um JSON com um valor boolean com o nome “success”. Se eu fizer:

eu obtenho

ao invés de

PERGUNTA 2: como gerar um JSON com vários objetos utilizando a serialização oferecida pelo VRaptor (e não manualmente via ‘metodo.json.jsp’) ?

Exemplo: quero gerar um JSON com um objeto Cliente e com um objeto Empresa. Se eu fizer:

result.use(json()).from(objCliente, "cliente").serialize(); result.use(json()).from(objEmpresa, "empresa").serialize();

eu obtenho

{"cliente": { ... }} {"empresa": { ... }}

ao invés de

{ "cliente": { ... }, "empresa": { ... } }

PERGUNTA 3: considerando o método manual de gerar JSON, sei que podemos passar o parâmetro _format=json na requisição, colocar o nosso template de JSON no caminho /WEB-INF/jsp/[recurso]/[metodo].json.jsp e setar os dados no JSON via método result.include. Existe alguma outra forma de se fazer isso? No guia do VRaptor encontrei uma outra forma através de alteração do "Header Accepts da requisição", mas não entendo como seria isso. Alguém poderia me mostrar um exemplo?

PERGUNTA 4: lendo sobre o VRaptor percebi que esse oferece suporte à serialização de objetos compostos, mas de forma explícita, via método include. Contudo, no VRaptor2, me lembro de uma anotação @Remotable que fazia com que essa serialização composta fosse implícita. Existe algo parecido assim no VRaptor3, como por exemplo alguma alteração na configuração padrão do VRaptor de modo que algo do tipo

serializasse implicitamente todos os campos de cliente? Na documentação vi falando algo sobre XStream e não sei se isso poderia ajudar nesse ponto. Se sim, alguém poderia me fornecer um exemplo?

Muito obrigado e peço desculpas pelas diversas perguntas, mas fiquei um tempo sem internet e as dúvidas acumularam-se.

Obrigado.

25 Respostas

Lucas_Cavalcanti

Tenta criar uma classe, pode ser interna:

pubic static class Success {
    private final boolean success;
    Success(boolean success) {
        this.success = success
    }
}
public void minhaLogica() {
    //...
    result.use(json()).from(new Success(true)).serialize();
}

O VRaptor usa o XStream pra gerar o json, e ele não suporta dar nomes pra primitivos diretamente…

Mesma coisa, crie uma classe interna:

public static class ClienteEmpresa {
   private Cliente cliente;
   private Empresa empresa;
}
public void minhaLogica() {
   //...
   result.use(json()).from(new ClienteEmpresa(cliente, empresa)).serialize();
}

Se vc não quer usar o _format, vc precisa usar o Header Accept… no browser, vc precisa usar javascript pra fazer isso, com um XMLHttpRequest, ou algo do tipo…
não tenho certeza se o <form accept=“json”…> funcionaria…

Não dá pra fazer implicitamente… pq não dá pra saber qdo parar… imagine que um Cliente tem um campo Usuário, que tem um campo cliente, etc… ia ficar em loop infinito! imagine tb que o cliente tem uma lista de produtos comprados… podem ser milhares de produtos, e esses produtos terem relacionamento com estoque e coisas do tipo… enfim… serializar tudo não é uma boa… vc tem que mandar pra view só aquilo que é relevante pra mostrar na tela

smillodont

Olá Lucas, muito obrigado pelas respostas. Complementando as perguntas:

Quanto à PERGUNTA 1, entendi a resposta, contudo, com a solução proposta, o JSON gerado será algo do tipo:

{"success": {"success": true}}

Existe alguma forma de omitir o nome do campo pai, via algo parecido com result.use(json()).from(new Success(true)).serialize(), de forma a obter o JSON:

{"success": true}

Isso seria importante pois estou utilizando ExtJS e esse é o formato esperado de resposta em uma submissão de formulário.

Quanto à PERGUNTA 2, você deu como resposta:

public static class ClienteEmpresa { private Cliente cliente; private Empresa empresa; } public void minhaLogica() { //... result.use(json()).from(new ClienteEmpresa(cliente, empresa)).serialize(); }

só para confirmar, na verdade, o correto seria o que segue abaixo, certo?

public static class ClienteEmpresa { private Cliente cliente; private Empresa empresa; } public void minhaLogica() { //... result.use(json()).from(new ClienteEmpresa(cliente, empresa)).include("cliente","empresa").serialize(); }

Quanto à PERGUNTA 4, entendi sua explicação sobre impossibilidade, mas não concordo. Entendo da necessidade de haver uma forma do usuário explicitamente incluir e excluir campos. Concordo que existem situações onde isso é muito importante. Contudo, se os objetos que o usuário gere a serem serializados devam (ou possam) ter todos os seus campos serializados (independentes desses serem primitivos ou não-primitivos), então seria bastante interessante que essa serialização recursiva e implícita fosse possível. Porque, assim, se esse objeto for alterado (adicionado um campo não-primitivo, por exemplo), o usuário não teria que se preocupar com a serialização: os dados chegariam todos no cliente e a preocupação seria restrita a alterações na view (JSP, JS, etc). O que acha :slight_smile: ?

Muito obrigado mais uma vez.

jingle

Oi tenho uma duvida também em relação ao Vraptor + json… vou me aproveita do tópico posso?

Como uso validator e Json? quero passar as mensagem que derram de erro via JSON.

tentei com onErroUse + Results.json, mas não sei em que objeto dar from pra obter as mensagens, e se passo uma string qualuer pra testar e mando serializar, da nullpointer quando não tiver erro no validator.

exemplo:
String notice = "deu pau"; validator.onErrorUse(Results.json()).from(notice, "notice").serialize();//isso da nullpointer quando não ouver erro no validator. mas minha intenção é passar as mensagens de erro e não uma string qualquer

Lucas_Cavalcanti

smillodont:
Olá Lucas, muito obrigado pelas respostas. Complementando as perguntas:

Quanto à PERGUNTA 1, entendi a resposta, contudo, com a solução proposta, o JSON gerado será algo do tipo:

{"success": {"success": true}}

Existe alguma forma de omitir o nome do campo pai, via algo parecido com result.use(json()).from(new Success(true)).serialize(), de forma a obter o JSON:

{"success": true}

Isso seria importante pois estou utilizando ExtJS e esse é o formato esperado de resposta em uma submissão de formulário.

Crie essa classe:

@Component
public class CustomJSONSerialization extends XStreamJSONSerialization {
    //delegate constructor

    @Override
    protected HierarchicalStreamDriver getHierarchicalStreamDriver() {
        return new JsonHierarchicalStreamDriver() {
            public HierarchicalStreamWriter createWriter(Writer writer) {
                return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
            }
        };
    }
}

Isso mesmo, erro meu =)

smillodont:

Quanto à PERGUNTA 4, entendi sua explicação sobre impossibilidade, mas não concordo. Entendo da necessidade de haver uma forma do usuário explicitamente incluir e excluir campos. Concordo que existem situações onde isso é muito importante. Contudo, se os objetos que o usuário gere a serem serializados devam (ou possam) ter todos os seus campos serializados (independentes desses serem primitivos ou não-primitivos), então seria bastante interessante que essa serialização recursiva e implícita fosse possível. Porque, assim, se esse objeto for alterado (adicionado um campo não-primitivo, por exemplo), o usuário não teria que se preocupar com a serialização: os dados chegariam todos no cliente e a preocupação seria restrita a alterações na view (JSP, JS, etc). O que acha :slight_smile: ?

Muito obrigado mais uma vez.


Acho que não tem problema fazer isso… crie uma issue lá no github, se possível em inglês, que a gente implementa =)

Abraço

Lucas_Cavalcanti

jingle:
Oi tenho uma duvida também em relação ao Vraptor + json… vou me aproveita do tópico posso?

Como uso validator e Json? quero passar as mensagem que derram de erro via JSON.

tentei com onErroUse + Results.json, mas não sei em que objeto dar from pra obter as mensagens, e se passo uma string qualuer pra testar e mando serializar, da nullpointer quando não tiver erro no validator.

exemplo:
String notice = "deu pau"; validator.onErrorUse(Results.json()).from(notice, "notice").serialize();//isso da nullpointer quando não ouver erro no validator. mas minha intenção é passar as mensagens de erro e não uma string qualquer


isso é um bug do vraptor =( vou corrigir assim q possível e te mando um snapshot…

smillodont

Olá Lucas, muito obrigado mais uma vez. Mas ainda ficou uma dúvida:

Quanto à PERGUNTA 2, você falou para implementar a classe CustomJSONSerialization. A implementei exatamente como você escreveu e abaixo segue o resultado final:

package teste;

import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.serialization.xstream.XStreamJSONSerialization;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;
import java.io.Writer;


@Component
public class CustomJSONSerialization extends XStreamJSONSerialization {
    //delegate constructor
    
    @Override
    protected HierarchicalStreamDriver getHierarchicalStreamDriver() {
        return new JsonHierarchicalStreamDriver() {
            public HierarchicalStreamWriter createWriter(Writer writer) {
                return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
            }
        };
    }
}

Mas a classe não compila, apresentando o seguinte erro:

[color=red]cannot find symbol
symbol : constructor XStreamJSONSerialization()
location: class br.com.caelum.vraptor.serialization.xstream.XStreamJSONSerialization
public class CustomJSONSerialization extends XStreamJSONSerialization {[/color]

Além disso, não entendi como seria o uso dessa classe. Se puder ajudar mais uma vez. Obrigado.

Lucas_Cavalcanti

aquele comentário //delegate constructor era pra vc gerar o construtor…

se vc estiver usando o eclipse é só dar Ctrl+1 em cima do erro… de qqer forma o construtor seria:

public CustomJSONSerialization(HttpServletResponse response, TypeNameExtractor extractor) {
    super(response, extractor);
}

e vc não precisa usar essa classe… o VRaptor que vai fazer isso

smillodont

Funcionou blz! Chick d+. Agora eu acho que dou esse post como resolvido, salvo a última pergunta: a solução que você propôs faz com que todo uso do tipo

omita o nome da raiz (‘campo’, no caso do código acima). Contudo, se em outro momento eu precisar desse nome do campo raiz, não vou ter essa informação. Ou seja, ou eu sempre tenho a raiz em todos os JSONs gerados como acima (caso default do VRaptor3) ou nunca tenho a raiz (caso onde se faz uso do CustomJSONSerialization citado), certo? Ou tem um meio do caminho (parametrizável, por exemplo) que eu não percebi?

Obrigado.

Lucas_Cavalcanti

não tem um meio caminho… pelo menos não “nativamente” no código do vraptor…

mas dá pra implementar classes parecidas como a CustomJSONSerialization e fazer isso:

result.use(JSONComRaizSerialization.class).from(...).serialize();
smillodont

Testei aqui, funcionou blz. Muito bom isso. Mas aí apareceu outra pergunta: se eu tenho várias classes parecidas com a CustomJSONSerialization, quando eu utilizar algo como

qual das classes CustomJSONSerialization será utilizada?

vlw

Lucas_Cavalcanti

vai dar uma exception falando q tem mtas classes… mas vc pode criar uma classe que tem métodos estaticos, pra vc fazer import:

public JSONs {
    public static Class<JSONSemRaizSerialization> jsonSemRaiz() { return JSONSemRaizSerialization.class; }
    
    public static Class<JSONComRaizSerialization> jsonComRaiz() { return JSONComRaizSerialization.class; }
    //...
}
Lucas_Cavalcanti

Lucas Cavalcanti:
jingle:
Oi tenho uma duvida também em relação ao Vraptor + json… vou me aproveita do tópico posso?

Como uso validator e Json? quero passar as mensagem que derram de erro via JSON.

tentei com onErroUse + Results.json, mas não sei em que objeto dar from pra obter as mensagens, e se passo uma string qualuer pra testar e mando serializar, da nullpointer quando não tiver erro no validator.

exemplo:
String notice = "deu pau"; validator.onErrorUse(Results.json()).from(notice, "notice").serialize();//isso da nullpointer quando não ouver erro no validator. mas minha intenção é passar as mensagens de erro e não uma string qualquer


isso é um bug do vraptor =( vou corrigir assim q possível e te mando um snapshot…

Bug corrigido… snapshot:
http://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.1.2-SNAPSHOT/vraptor-3.1.2-20100305.215218-3.jar

jingle

Lucas Cavalcanti:
Lucas Cavalcanti:
jingle:
Oi tenho uma duvida também em relação ao Vraptor + json… vou me aproveita do tópico posso?

Como uso validator e Json? quero passar as mensagem que derram de erro via JSON.

tentei com onErroUse + Results.json, mas não sei em que objeto dar from pra obter as mensagens, e se passo uma string qualuer pra testar e mando serializar, da nullpointer quando não tiver erro no validator.

exemplo:
String notice = "deu pau"; validator.onErrorUse(Results.json()).from(notice, "notice").serialize();//isso da nullpointer quando não ouver erro no validator. mas minha intenção é passar as mensagens de erro e não uma string qualquer


isso é um bug do vraptor =( vou corrigir assim q possível e te mando um snapshot…

Bug corrigido… snapshot:
http://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.1.2-SNAPSHOT/vraptor-3.1.2-20100305.215218-3.jar

Valeuu!!

agora como faço pra pegar as mensagens de erro no validator? pra mim poder manda pra tela?

Lucas_Cavalcanti

http://vraptor.caelum.com.br/documentacao/validacao/

se vc usou tudo direitinho, o próprio vraptor coloca uma variável ${errors} na jsp que tem os erros

smillodont

Obrigado Lucas. Tópico resolvido. Além disso, já adicionei um tópico no github conforme você solicitou (sobre serialização recursiva implícita).

Parabéns pelo VRaptor e pela disponibilidade aqui no fórum! Espero em breve poder contribuir com o fórum também. Obrigado.

Lucas_Cavalcanti

implementado:
http://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.1.2-SNAPSHOT/vraptor-3.1.2-20100310.010227-4-sources.jar

se quiser o snapshot está acima

[]'s

smillodont

Já implementaram a recursão? Que isso cara, vcs são foda! Vlw d+.

Acrescento a forma de uso da serialização recursiva que recebi via GitHub:

Contudo, não consegui compilar o snapshot. Eu utilizo NetBeans 6.5. Como faço para gerar o .jar com as classes compiladas?

Obrigado.

Lucas_Cavalcanti

ops, te passei o link errado, o certo é esse:
http://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.1.2-SNAPSHOT/vraptor-3.1.2-20100310.010227-4.jar

smillodont

Agora funcionou. Testei aki. Funfou blz!

Mas agora apareceu outra dúvida, percebi que a serialização desconsidera campos que estejam null (pelo menos Strings e Dates foram desconsiderados no JSON). Existe alguma forma de fazer com que esses campos null sejam considerados na serialização, escrevendo, no caso, o valor null?

Obrigado.

Lucas_Cavalcanti

o XStream não suporta isso… pelo menos não sem várias configurações…

pq vc quer imprimir os nulls tb?

smillodont

Foi mais por curiosidade, mas também para poder conhecer mais das configurações do framework. Na empresa onde trabalho estamos em vias de iniciar uma nova versão do sistema e uma das opções cogitadas para isso é o VRaptor3, então estou tentando colher o máximo de informação. Não há a necessidade de nulls, dá para fazer o tratamento no cliente de outras formas.

Muito obrigado mais uma vez e parabéns pelo trabalho.

Lucas_Cavalcanti

sem problemas…

é que geralmente os clientes já interpretam campo inexistente como null…

vc provavelmente não vai precisar fazer nada de mais…

só não faça parsing de JSON na mão… toda linguagem de programação já tem isso pronto pra vc…

sry, não era pra ser uma msg mal educada =(

Abraços, qqer dúvida é só falar

smillodont

Sua mensagem não foi mal educada. Muito obrigado pelas dicas. Abraço, vlw.

Lavieri

Eu precisei implmentar isso de gerar um JSON sem um alias pro root, fiz isso com a ajuda do lucas, e deste post.

padronizei que, alias com uma stirng empty, seria o ROOT nulo,

result.use(Results.json()).from(object,"").serialize(); //ROOT jason nulo

result.use(Results.json()).from(object,“alias”).serialize(); //ROOT jason alias

segue o codigo

http://pastebin.com/Rv7AenvW

smillodont

Olá Lavieri,

jóia a solução. Já peguei o código e vou usar no meu projeto.

Vlw.

Criado 2 de março de 2010
Ultima resposta 11 de mar. de 2010
Respostas 25
Participantes 4