Vraptor, como formatar datas em um json?

Olá caros amigos, gostaria de formatar as datas retornadas pelo vRaptor. Ele está retornando datas como se fossem objetos. Alguém sabe como posso formatar datas?

{"success": true, "data": [ { "id": 150, "nome": "Maria das Dores", "dataNascimento": { "@class": "sql-timestamp", "$": "2011-04-16 00:00:00.0" }, "cpfCnpj": 123123, "rg": 0, "dataExpedicao": { "@class": "sql-timestamp", "$": "2011-01-31 00:00:00.0" }, "orgaoExpedidor": "", "limiteCredito": 0.0, "logradouro": "", "numero": 0, "bairro": "", "cidade": { "id": 126, "nome": "Cutias", "estado": { "id": 3, "sigla": "AP", "nome": "Amapá" } } ...} }

você pode sobrescrever o jsonSerialization, como nesse gist:

É só criar essa classe no meu projeto e pronto? Não preciso fazer nenhuma indicação em uma annotation?

@Component nela é o suficiente

Não deu certo. :frowning:

Olha meu a função que retorna o json:

public void lista(){ List<Cliente> clientes = clienteDao.findAll(); result.use(ExtJSJson.class).from(clientes).success(true).serialize(); }

Detalhe, esse clienteDao está sendo injeto pelo Spring e o Controler é gerenciado pelo vraptor. Tem algum problema?

então ao invés de sobrescrever o XStreamJSONSerialization, sobrescreva ExtJSJsonImpl…

o que eu te passei funcionaria se vc usasse result.use(json())

Tu já viu como está a classe ExtJSJsonImpl?

Não cosigo setar o atributo xstream pq ele não está encapsulado. Não tem como reutilizar essa componente.

Se tivesse bastava fazer:

@Component
public class CustomJSONSerialization extends ExtJSJsonImpl {
     
       
      public CustomJSONSerialization(HttpServletResponse response) {
		super(response);
		getXtream().registerConverter(new DateConverter("dd/MM/yyyy", new String[0]));
      }


} 

opa, sry…

o ExtJSJSon foi uma contribuição externa, então não seguiu muito o jeito normal do VRaptor de programar…

o que vc pode fazer é copiar essa classe para o seu projeto:

https://github.com/caelum/vraptor/blob/master/vraptor-core/src/main/java/br/com/caelum/vraptor/util/extjs/ExtJSJsonImpl.java

e mudar o xstream do construtor adicionando o seu converter…

o ideal era que essa classe usasse o JSONSerialization

já tem uma issue pra fazer isso: https://github.com/caelum/vraptor/issues/292

na próxima versão isso vai estar mais configurável

É, o negócio lá tá parecendo uma briga de faca aqui do interior daqui do Ceará.

Lucas, não funcionou. Após as implementações abaixo, continua não formatando a data. É algo que eu estou fazendo errado?

mesmo fazendo isso:

public void lista(){ List<Cliente> clientes = clienteDao.findAll(); result.use(json()).from(clientes, "data").serialize(); }

@Component
public class CustomJSONSerialization extends XStreamJSONSerialization {
      
    public CustomJSONSerialization(HttpServletResponse response,TypeNameExtractor extractor, ProxyInitializer initializer) {
        super(response, extractor, initializer);
    }
       
    @Override
    public XStream getXStream() {
        XStream xstream = super.getXStream();
        xstream.registerConverter(new DateConverter("dd/MM/yyyy", new String[0]));
        return xstream;
    }
  
} 

a sua data é um Date ou um Calendar? é Date do java.util ou java.sql?

java.util.Date

Taqui a criança:

[code]package br.com.drover.entity;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;

import org.springframework.beans.factory.annotation.Autowired;

@Entity
@SequenceGenerator(name=“CLIENTE_S”,initialValue=1,sequenceName=“CLIENTE_S”)
public class Cliente {

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="CLIENTE_S")
private Integer id;

private String nome;

private Boolean isPessoaJuridica;

private Date dataNascimento;

private Long cpfCnpj;

private Long rg;

private Date dataExpedicao ;

private String orgaoExpedidor;

private Double limiteCredito;

private String logradouro;

private Integer numero;

private String bairro;

private String CEP;

private String nomePai;

private String nomeMae;

@ManyToOne
private Cidade cidade;

public Cliente() {
	
}

public Cliente(Integer  id) {
	this.id = id;
}

public String getNome() {
	return nome;
}

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


public Integer getId() {
	return id;
}

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

public Long getCpfCnpj() {
	return cpfCnpj;
}

public void setCpfCnpj(Long cpfCnpj) {
	this.cpfCnpj = cpfCnpj;
}

public String getLogradouro() {
	return logradouro;
}

public void setLogradouro(String logradouro) {
	this.logradouro = logradouro;
}

public String getBairro() {
	return bairro;
}

public void setBairro(String bairro) {
	this.bairro = bairro;
}

public Cidade getCidade() {
	return cidade;
}

@Autowired
public void setCidade(Cidade cidade) {
	this.cidade = cidade;
}

public Double getLimiteCredito() {
	return limiteCredito;
}

public void setLimiteCredito(Double limiteCredito) {
	this.limiteCredito = limiteCredito;
}

public String getNomePai() {
	return nomePai;
}

public void setNomePai(String nomePai) {
	this.nomePai = nomePai;
}

public String getNomeMae() {
	return nomeMae;
}

public void setNomeMae(String nomeMae) {
	this.nomeMae = nomeMae;
}

public Long getRg() {
	return rg;
}

public void setRg(Long rg) {
	this.rg = rg;
}

public String getOrgaoExpedidor() {
	return orgaoExpedidor;
}

public void setOrgaoExpedidor(String orgacaoExpedidor) {
	this.orgaoExpedidor = orgacaoExpedidor;
}

public Boolean getIsPessoaJuridica() {
	return isPessoaJuridica;
}

public void setIsPessoaJuridica(Boolean isPessoaJuridica) {
	this.isPessoaJuridica = isPessoaJuridica;
}

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

public void setCEP(String cEP) {
	CEP = cEP;
}

public String getCEP() {
	return CEP;
}

public void setNumero(Integer numero) {
	this.numero = numero;
}

public Integer getNumero() {
	return numero;
}

public Date getDataExpedicao() {
	return dataExpedicao;
}

public void setDataExpedicao(Date dataExpedicao) {
	this.dataExpedicao = dataExpedicao;
}

public Date getDataNascimento() {
	return dataNascimento;
}

public void setDataNascimento(Date dataNascimento) {
	this.dataNascimento = dataNascimento;
}

}
[/code]

troca o:

xstream.registerConverter(new DateConverter("dd/MM/yyyy", new String[0])); 

por

xstream.registerConverter(new SingleValueConverter() {

   public String toString(Object value) {
         return new SimpleDateFormat("dd/MM/yyyy").format(value);
   }

   public boolean canConvert(Class clazz) {
        return Date.class.isAssignableFrom(clazz);
   }

   public Object fromString(String value) {
        return null; //não é usado
   }
}); 

Lucas, percebi que o Hibernate está retornando um Timestamp no lugar de um Date. Acho que deve ser por isso que ainda não funcionou. Segue screemshot.


mas se o seu campo está declarado como date não deveria ter problema…

vc colocou o converter implementado como eu te falei? importou de java.util.Date?

Aindan não deu. Ele converte, mas converte dentro de uma estrutura própria. Como se fosse um objeto:

“dataNascimento”: {"@class": “sql-date”,"$": “2011-03-30”},“cpfCnpj”: 1296231305,“rg”: 2002001345512,“dataExpedicao”: {"@class": “sql-date”,"$": “2011-06-27”}

só pra confirmar, seu custom está assim?

import java.util.Date;
import br.com.caelum.vraptor.ioc.Component;

@Component  
public class CustomJSONSerialization extends XStreamJSONSerialization {  
        
    public CustomJSONSerialization(HttpServletResponse response,TypeNameExtractor extractor, ProxyInitializer initializer) {  
        super(response, extractor, initializer);  
    }  
         
    @Override  
    public XStream getXStream() {  
        XStream xstream = super.getXStream();  
        xstream.registerConverter(new   SingleValueConverter() {  
  
           public String toString(Object value) {  
                 return new  SimpleDateFormat("dd/MM/yyyy").format(value);  
           }  
  
           public boolean canConvert(Class clazz) {  
                return Date.class.isAssignableFrom(clazz);  
           }  
  
           public Object fromString(String value) {  
              return null; //não é usado  
           }  
        });   
        return xstream;  
    }  
    
}   

inclusive os imports?
o que me parece é que não está passando por essa classe…

Meu custon está assim e está sendo executado. Já executei e confirmei isso. Amanhã eu vou fazer um upload do projeto para o github pra você ver como está tudo certinho.