Blog

Como utilizar JSON-B (JSR 367)

Como utilizar JSON-B (JSR 367)
Publicado por: Gerardo Arroyo Arce

En este artículo haremos uso del nuevo JSON-B JSR 367 que fue liberado oficialmente a mitad del 2017.

JSON-B es una especificación standard de Java que nos permite convertir objeto hacia y desde mensajes JSON empleando un API de alto nivel y sin tener -necesariamente- conocimientos más profundos respecto al formato de JSON y su especificación. No debe confundirse con JSON-P, el cual constituye un API para procesar (parsear, generar, transformar, consultar) mensajes JSON.

Una de sus principales ventajas es que es consistente con JAXB y otros APIs de JavaEE y es muy simple de usar.

El código fuente de este artículo esta disponible en https://github.com/FlechaRoja/JSON-B

POM

Para proceder a usar JSON-B necesitamos agregar algunas dependencias a nuestro POM.
{% highlight xml %}
<dependencies>
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
<version>1.0</version>
</dependency>

   <dependency>
       <groupId>org.eclipse</groupId>
       <artifactId>yasson</artifactId>
       <version>1.0</version>
   </dependency>

   <dependency>
       <groupId>org.glassfish</groupId>
       <artifactId>javax.json</artifactId>
       <version>1.1</version>
   </dependency>

</dependencies>
{% endhighlight %}

Serializar y Deserializar

Vamos a suponer que tenemos una clase Clientes, la cual consta de varios atributos. Incluyendo una fecha de nacimiento usando el nuevo Date-Time API introducido en Java 8.
{% highlight java %}
public class Clientes {
private String nombre;
private String primerApellido;
private String segundoApellido;
private LocalDate fechaNacimiento;
private Boolean activo;

private List<Telefonos> telefonos;
{% endhighlight %}

Y a su vez, un cliente tiene una colección de ‘n’ Telefonos.
{% highlight java %}
public class Telefonos {
private String codPais;
private String numero;

{% endhighlight %}

Y deseamos poder convertir ese objeto a una representación JSON. Para ello se realiza lo siguiente:

{% highlight java %}
Jsonb jsonb = JsonbBuilder.create();
String result = jsonb.toJson(cliente);
{% endhighlight %}

El valor que obtenemos en result es el siguiente:

{% highlight json %}
{“activo”:true,“fechaNacimiento”:“1975-01-01”,“nombre”:“Juan”,“primerApellido”:“Perez”,“segundoApellido”:“Perez”}
{% endhighlight %}

El proceso es extremadamente simple y no requiere de ninguna modificación a las clases Clientes y Telefonos.

¿Cómo construimos una instancia de Clientes con base en ese String? De manera trivial:

{% highlight java %}
cliente = jsonb.fromJson(result, Clientes.class);
{% endhighlight %}

Ahora bien, si la colección de Telefonos tuviera valores el proceso de serialización y deserialización es el mismo; pero el JSON que se genera varia de la siguiente manera:

{% highlight java %}
Telefonos tel = new Telefonos();
tel.setCodPais(“506”);
tel.setNumero(“22222222”);
cliente.addTelefono(tel);

tel = new Telefonos();
tel.setCodPais(“506”);
tel.setNumero(“1111111”);
cliente.addTelefono(tel);
{% endhighlight %}

Y el JSON es:
{% highlight json %}
{“activo”:true,“fechaNacimiento”:“1975-01-01”,“nombre”:“Juan”,“primerApellido”:“Perez”,“segundoApellido”:“Perez”,
“telefonos”:[{“codPais”:“506”,“numero”:“22222222”},{“codPais”:“506”,“numero”:“1111111”}]}
{% endhighlight %}

Opciones Avanzadas

Si deseamos comenzar a manipular la forma en que se genera el JSON, solo debemos establecer la configuración que sea de nuestro interés.

Formato y Nomenclatura

Supongamos que queremos que el nombre de los atributos de nuestras clases sea en minúsculas y separados por un guión; y también que tengan un formato más legible para un ser humano.

{% highlight java %}
JsonbConfig config = new JsonbConfig()
.withFormatting(true)
.withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES);

Jsonb jsonb = JsonbBuilder.create(config);
String result = jsonb.toJson(cliente);
{% endhighlight %}

Y el JSON resultante sería:
{% highlight json %}
{
“activo”: true,
“fecha-nacimiento”: “1975-01-01”,
“nombre”: “Juan”,
“primer-apellido”: “Perez”,
“segundo-apellido”: “Perez”,
“telefonos”: [
{
“cod-pais”: “506”,
“numero”: “22222222”
},
{
“cod-pais”: “506”,
“numero”: “1111111”
}
]
}
{% endhighlight %}

Existen 6 estrategias de nomenclatura, que son:

  • IDENTITY (primerApellido) Esta se emplea por omisión.

  • LOWER_CASE_WITH_DASHES (primer-apellido)

  • LOWER_CASE_WITH_UNDERSCORES (primer_apellido)

  • UPPER_CAMEL_CASE (PrimerApellido)

  • UPPER_CAMEL_CASE_WITH_SPACES (Primer Apellido)

  • CASE_INSENSITIVE (pRiMeRaPeLlIdO) (suponiendo que así se nombre a la propiedad)

  • O tu propia implementación de la interfaz JsonbNamingStrategy

Ordenamiento

¿Qué sucede si queremos cambiar el orden en el cual se genera cada atributo?
Nuevamente es solo modificar la configuración de nuestro interés; en este caso pasamos del ordenamiento por omisión lexicográfico al ‘Any’.

{% highlight java %}
JsonbConfig config = new JsonbConfig()
.withFormatting(true)
.withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES)
.withPropertyOrderStrategy(PropertyOrderStrategy.ANY);
{% endhighlight %}

Y el JSON resultante sería:
{% highlight json %}
{
“primer-apellido”: “Perez”,
“fecha-nacimiento”: “1975-01-01”,
“segundo-apellido”: “Perez”,
“telefonos”: [
{
“numero”: “22222222”,
“cod-pais”: “506”
},
{
“numero”: “1111111”,
“cod-pais”: “506”
}
],
“nombre”: “Juan”,
“activo”: true
}
{% endhighlight %}

Existen 3 estrategias de ordenamiento de las propiedades:

  • LEXICOGRAPHICAL (A-Z) Es la que se usa por omisión.

  • ANY (No esta definido el ordenamiento, dependería de la implementación, generalmente va de acuerdo al orden en el que se definieron las propiedades)

  • REVERSE (Z- A)

Manejo de Nulos

El comportamiento por omisión de JSON-B es tal que las propiedades con valor null no son representadas en el JSON. En ocasiones eso no es deseable, pero se requiere de solo una instrucción para cambiar ese accionar.

{% highlight java %}
Clientes cliente = new Clientes();
cliente.setFechaNacimiento(LocalDate.parse(“1975-01-01”));
cliente.setNombre(“Juan”);
cliente.setPrimerApellido(“Perez”);
cliente.setSegundoApellido(“Perez”);
cliente.setActivo(Boolean.TRUE);

JsonbConfig config = new JsonbConfig()
.withFormatting(true)
.withNullValues(true) // Indicamos que los valores en null se representen.
.withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES);

Jsonb jsonb = JsonbBuilder.create(config);
String result = jsonb.toJson(cliente);
{% endhighlight %}

Y el JSON resultante sería:
{% highlight json %}
{
“activo”: true,
“blob”: null,
“fecha-nacimiento”: “1975-01-01”,
“nombre”: “Juan”,
“primer-apellido”: “Perez”,
“segundo-apellido”: “Perez”,
“telefonos”: null
}
{% endhighlight %}

Cambiar el nombre de una propiedad

Supongamos que necesitamos cambiar el nombre de una propiedad al generar el JSON. En nuestro ejemplo, necesitamos que el atributo ‘codPais’ de la clase Telefonos se genere con el nombre ‘codigo-pais’.

Para hacer el cambio solo necesitamos agregar la anotación @JsonbProperty

{% highlight java %}
public class Telefonos {
@JsonbProperty(“codigo-pais”)
private String codPais;
private String numero;
{% endhighlight %}

Y el JSON resultante sería:
{% highlight json %}
{
“activo”: true,
“fechaNacimiento”: “1975-01-01”,
“nombre”: “Juan”,
“primerApellido”: “Perez”,
“segundoApellido”: “Perez”,
“telefonos”: [
{
“codigo-pais”: “506”,
“numero”: “22222222”
},
{
“codigo-pais”: “506”,
“numero”: “1111111”
}
]
}
{% endhighlight %}

Modificar la representación de la Fecha

En el caso de que el formato de las fechas no sea el que necesitamos, se puede establecer otro diferente por medio de la anotación @JsonbDateFormat

{% highlight java %}
@JsonbDateFormat(“dd.MM.yyyy”)
private LocalDate fechaNacimiento;
{% endhighlight %}

Y el JSON resultante sería:
{% highlight json %}
{
“activo”: true,
“fechaNacimiento”: “01.01.1975”,
“nombre”: “Juan”,
“primerApellido”: “Perez”,
“segundoApellido”: “Perez”,
“telefonos”: [
{
“codigo-pais”: “506”,
“numero”: “22222222”
},
{
“codigo-pais”: “506”,
“numero”: “1111111”
}
]
}
{% endhighlight %}

Codificación de Binarios

Afortunadamente JSON-B permite el soporte nativo de datos binarios. Por omisión se usa el ‘encoding’ BYTE.

Agregamos un byte[] en Clientes.
{% highlight java %}
public class Clientes {

private String nombre;
private String primerApellido;
private String segundoApellido;

@JsonbDateFormat("dd.MM.yyyy")
private LocalDate fechaNacimiento;
private Boolean activo;

// Dato binario
private byte[] blob;

{% endhighlight %}

Y para generar el JSON, se agrega esta configuración:
{% highlight java %}
Clientes cliente = new Clientes();
cliente.setFechaNacimiento(LocalDate.parse(“1975-01-01”));
cliente.setNombre(“Juan”);
cliente.setPrimerApellido(“Perez”);
cliente.setSegundoApellido(“Perez”);
cliente.setActivo(Boolean.TRUE);
cliente.setBlob(“prueba de encoding”.getBytes());

JsonbConfig config = new JsonbConfig()
.withFormatting(true)
.withNullValues(true)
.withBinaryDataStrategy(BinaryDataStrategy.BASE_64) // Encoding
.withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES);

Jsonb jsonb = JsonbBuilder.create(config);
String result = jsonb.toJson(cliente);
{% endhighlight %}

Y el JSON resultante sería:
{% highlight json %}
{
“activo”: true,
“blob”: “cHJ1ZWJhIGRlIGVuY29kaW5n”,
“fecha-nacimiento”: “01.01.1975”,
“nombre”: “Juan”,
“primer-apellido”: “Perez”,
“segundo-apellido”: “Perez”,
“telefonos”: null
}
{% endhighlight %}

Existen 3 estrategias de codificación:

  • BYTE Es la que se usa por omisión.

  • BASE_64

  • BASE_64_URL

 

Conclusión

La incorporación de JSON-B permite hacer uso de un API de alto nivel que facilita notoriamente el consumo y generación de mensajes JSON.

Su semejanza con el resto de APIs standard de JavaEE permite a los desarrolladores tener una curva de aprendizaje mínima y maximar su productividad.

Referencias
Como utilizar JSON-B (JSR 367)

Gerardo Arroyo Arce

CEO & Co-Founder

  • ​AWS Community Builder & Ambassador
  • ​AWS Solution Architect - Professional
  • AWS Certified Database – Specialty
  • AWS Certified Security – Specialty
  • AWS Solution Architect - Associate
  • AWS Certified Developer Associate
  • AWS Certified SysOps Administrator Associate
  • Ingeniero en Software. ITCR.
  • Master en Computación en Informática. UCR.