Vamos a ver como podemos integrar la librería Jackson,
En la siguiente url encontraremos un proyecto con la configuración que hemos puesto en este blog https://github.com/blancoparis-tfc/SpringJson
La anotación @DpbLog es una anotación para poder trazear las llamadas al método, viene explicado en el articulo anterior.
Apartados:
- Configurar jackson en spring.
- Funcionamiento de las vistas de un json en spring.
- Soporte de jackson a XML
- Personalizar jackson en spring.
- Configurar JSR-330 y como establecer los formateo de las fechas.
Configurar jackson 2 con spring 4.1
Esta parte es bastante fácil, ya que simplemente tenemos que poner las librerías y estas automáticamente se integran en spring, con una configuración por defecto.
- Añadimos la dependencia de jackson core.
- Añadimos la dependencia de jackson databind
Es importante que estén las dos dependencias para que funcione correctamente.
compile 'com.fasterxml.jackson.core:jackson-core:2.5.3'
compile 'com.fasterxml.jackson.core:jackson-databind:2.5.3'
Objeto que vamos a serializar:
package org.dbp.controller.json;
import java.io.Serializable;
public class EjemploJson implements Serializable{
private Long numero;
private String cadena;
public Long getNumero() {
return numero;
}
public void setNumero(Long numero) {
this.numero = numero;
}
public String getCadena() {
return cadena;
}
public void setCadena(String cadena) {
this.cadena = cadena;
}
@Override
public String toString() {
return "EjemploJson [numero=" + numero + ", cadena=" + cadena + "]";
}
}
Ejemplo de uso en el controlador.
@DbpLog
@RequestMapping("ejemploJson")
public @ResponseBody EjemploJson ejemploJson(){
EjemploJson valdev=new EjemploJson();
valdev.setNumero(1L);
valdev.setCadena("cadena");
return valdev;
}
Este controlador nos devolverá el siguiente resultado:
{"numero":1,"cadena":"cadena"}
Vistas Json
Spring ahora nos proporciona un mecanismos que nos permite filtrar que partes de un objeto:
La idea es parecida a la de los grupos que se utilizan en el estándar de java para las validaciones.
- Para clasificar usaremos interfaces definidos.
- Y luego los campos que nos interesa filtrar usaremos la anotación @JsonView.
Ejemplo de objeto que vamos a serializar filtrado:
package org.dbp.controller.json;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonView;
public class EjemploJson implements Serializable{
public interface Resumen{};
@JsonView(Resumen.class)
private Long numero;
private String cadena;
public Long getNumero() {
return numero;
}
public void setNumero(Long numero) {
this.numero = numero;
}
public String getCadena() {
return cadena;
}
public void setCadena(String cadena) {
this.cadena = cadena;
}
@Override
public String toString() {
return "EjemploJson [numero=" + numero + ", cadena=" + cadena + "]";
}
}
Ahora vamos a ver como decirle al controlador que nos pinte los campos de la vista , poniendo la misma anotación:
@JsonView(Resumen.class)
@DbpLog
@RequestMapping("resumenJson")
public @ResponseBody EjemploJson resumenJson(){
return mockEjemplo();
}
private EjemploJson mockEjemplo() {
EjemploJson valdev=new EjemploJson();
valdev.setNumero(1L);
valdev.setCadena("cadena");
return valdev;
}
Si no ponemos la anotación nos pone todos los campos.
Y nos devolverá el siguiente json como resultado:
{"numero":1}
Vamos a ver como utilizar estas vistas de manera conjunta, como la anotación @JsonView solo se puede poner una utilizaremos las herencias para asociar dos. De esta manera si ponemos la herencia hija, en el controlador activara todas las vistas que hereden.
Ejemplo de objeto que vamos a serializar filtrado:
package org.dbp.controller.json;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonView;
public class EjemploJson implements Serializable{
public interface Resumen{};
public interface Resumen2 extends Resumen{}
@JsonView(Resumen.class)
private Long numero;
private String cadena;
@JsonView(Resumen2.class)
private String descripcion;
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public Long getNumero() {
return numero;
}
public void setNumero(Long numero) {
this.numero = numero;
}
public String getCadena() {
return cadena;
}
public void setCadena(String cadena) {
this.cadena = cadena;
}
@Override
public String toString() {
return "EjemploJson [numero=" + numero + ", cadena=" + cadena + "]";
}
}
Ahora vemos como utilizarlo en el controlador
@JsonView(Resumen2.class)
@DbpLog
@RequestMapping("resumen2Json")
public @ResponseBody EjemploJson resumen2Json(){
return mockEjemplo();
}
private EjemploJson mockEjemplo() {
EjemploJson valdev=new EjemploJson();
valdev.setNumero(1L);
valdev.setCadena("cadena");
valdev.setDescripcion("descripcion");
return valdev;
}
Y nos devolverá el siguiente json como resultado:
{"numero":1,"descripcion":"descripcion"}
SpringMVC tiene configurado por defecto a true la siguiente propiedad MapperFeature.DEFAULT_VIEW_INCLUSION , esto significa que los campos que no tengan la anotación se agregaran automáticamente
Soporte xml
Aquí vamos a ver como la librería Jackson nos da soporte de xml:
Ventajas de activar la parte de xml de la librería de Jackson:
- Nos permite utilizar las anotaciones de jackson y las de JAXB.
- Podemos utilizar el mismo objeto para configurar un xml y json, sin tener que tener dos objetos o configuraciones separadas.
- Es capaz de generar un xml, sin las anotaciones de jaxb.
Al activar la librería spring, al activar esta parte en jackson sustituirá a jaxb.
Ahora vamos a ver los pasos que tenemos que dar para poder configurar-lo en el proyecto, simplemente al igual que jackson al incluir la librería se configurara por defecto.
// Jackson soporte xml
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.5.3'
Como podemos indicarle a un controlador, que vamos a devolver un xml, esto es fácil, ya que solo le indicamos en el mapeo que vamos a producir un xml.
@DbpLog
@RequestMapping(value="ejemploXml",produces = "application/xml")
public @ResponseBody EjemploJson ejemploXml(){
return mockEjemplo();
}
Y nos devolvera el siguiente xml como resultado:
<EjemploJson xmlns="">
<numero>1</numero>
<cadena>cadena</cadena>
<descripcion>descripcion</descripcion>
</EjemploJson>
Una vez indicado esto tenemos que indicar en todas las configuraciones si va a devolver un Json o un xml.
Como personalizar Jackson en Spring:
Para poder personalizar Jackson en spring, ahora podemos utilizar el objeto Jackson2ObjectMapperBuilder
Vamos a establecer el formateo de las fechas al formato español 'dd-MM-yyyy' y identamos el json que devuelve el servidor.
Ahora vamos a ver como activar la configuración de jackson:
- identiOutput: Tabulamos la salida del json.
- dateFormat: Le pondremos la fecha "dd-MM-yyyy"
Tenemos que pasarle los converts de jackson a spring, para que lo sepa esto siempre que tengamos que realizar la configuración.
@Override
public void configureMessageConverters(
List> converters) {
super.configureMessageConverters(converters);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new SimpleDateFormat("dd-MM-yyyy"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
...
Configurar el JSR-330, un formato de fecha determinado
Ahora vamos a ver como poner por defecto un formato de fecha concreto, esto es muy tipico en cualquier proyecto, en España ya que el formato es distinto al ingles.
En este caso, vamos a seguir los siguientes pasos, para el caso LocalDate:
- Crearemos una clase para deserializar.
- Crearemos una clase para serializar.
- Tendremos que crear un modulo propio, ya que el modulo de JSR330 no tiene ningún mecanismos para escalarlo.
- Por ultimo lo tendremos que registrar.
Ojo, a la hora registrarlo tenemos que hacerlo después del build, ya que la operación de build lo que hace es volver a cargar los deserealizadores y serializadores del JSR330 y el valido es el ultimo que llega.
private class LocalDateDeserializerEs extends LocalDateDeserializer{
private LocalDateDeserializerEs(){
super(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
}
private class LocalDateSerializerEs extends LocalDateSerializer{
private LocalDateSerializerEs(){
super(false,DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
}
private class JSR310ModuleEs extends SimpleModule{
public JSR310ModuleEs() {
super(PackageVersion.VERSION);
addDeserializer(LocalDate.class,new LocalDateDeserializerEs());
addSerializer(LocalDate.class, new LocalDateSerializerEs());
}
}
@Override
public void configureMessageConverters(
List> converters) {
super.configureMessageConverters(converters);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder
.indentOutput(true)
.dateFormat(new SimpleDateFormat("dd-MM-yyyy")
);
converters.add(new MappingJackson2HttpMessageConverter(
builder
.build()
.registerModule(new JSR310ModuleEs())));
converters.add(new MappingJackson2XmlHttpMessageConverter(
builder.createXmlMapper(true)
.build()
.registerModule(new JSR310ModuleEs())));
}
El resultado de esta configuración:
En JSON
{
"numero" : 1,
"cadena" : "cadena",
"descripcion" : "descripcion",
"fecha" : "01-05-2015",
"fechaLocal" : "01/05/2015",
"fechaTime" : "2015-05-01T21:38:45.748"
}
En Xml
<EjemploJson>
<numero>1</numero>
<cadena>cadena</cadena>
<descripcion>descripcion</descripcion>
<fecha>02-05-2015</fecha>
<fechaLocal>02/05/2015</fechaLocal>
<fechaTime>2015-05-02T10:40:21.485</fechaTime>
</EjemploJson>
Podemos ver que la fecha que es un date, se formatea con los guiones la fechaLocal es un LocalDate y se formatea con los /. y el LocalDateTime, no lo hemos puesto un formateo y por eso sale asi. (Si seguimos los mismos pasos que al anterior podemos poner el que nos interese).
Nota: Al registrar un serializador mas de una vez sobre la misma clase, la ultima es la que prevalece, pero hay que tener en cuenta que los de JSR330 se registran cuando se realiza el Build.