JSON (JavaScript Object Notation) es un formato ligero para el intercambio de datos. Es un subconjunto de la notación literal de objetos de JavaScript. JSON es sencillo de escribir, sencillo de leer, sencillo de manipular, y en la mayoría de los casos es mucho más liviano que XML.
JSON maneja dos estructuras básicas:
- Colección de pares nombre/valor, conocidas en varios lenguajes como Objetos, Registros, Estructuras, Diccionarios, Mapas, Arrays Asociativos
- Lista ordenada de valores, conocidas en varios lenguajes como Arrays, Vectores, Listas, Secuencias
Para facilitar la comprensión, llamaremos a las primeras
Mapas u
Objetos, indistintamente, y a las segundas
Listas o
Arrays. En Java los Mapas se pueden corresponder con instancias concretas de la clase
java.util.Map o con simples
Java Beans (objetos Java sin métodos y con setters y getters para cada uno de los atributos). A la vez, las Listas se pueden asociar con instancias concretas de la clase
java.util.List o meros
Arrays que mantienen ordenados sus elementos.
En la notación JSON los Mapas comienzan con llave de apertura "{" y terminan con llave de cierre "}". Tanto los Mapas como las Listas son colecciones de elementos. En el primer caso los elementos son pares nombre/valor; en el segundo, solamente valores. Los dos elementos de un par (nombre y valor) están separados por dos puntos ":" y los nombres, o claves para seguir con la terminología de Mapas, siempre tienen que ser del tipo String.

Las Listas comienzan con corchete de apertura "[" y terminan con corchete de cierre "]" y sus elementos, como mencioné antes, están separados por comas ",".

Por último, los valores pueden ser del tipo string, number, object (Mapa), array (Lista), booleano (true/false) o null.

Veamos algunos ejemplos.
Definamos la clase
Persona con sus atributos "nombre", "apellido", "fechaNacimiento", "soltero", "cantHijos":
public class Persona {
private String nombre;
private String apellido;
private Date fechaNacimiento;
private Boolean soltero;
private Integer cantHijos;
// ... Los setters y getters para cada atributo ...
}Carguemos un objeto
Persona:
Persona persona = new Persona();
persona.setNombre("Adrian");
persona.setApellido("Paredes");
persona.setFechaNacimiento(parseDate("03/06/82", "dd/MM/yy"));
persona.setSoltero(false);
persona.setCantHijos(0);
Una forma equivalente de guardar este objeto sería en un
HashMap:
Map persona = new HashMap();
persona.put("nombre", "Adrian");
persona.put("apellido", "Paredes");
persona.put("fechaNacimiento", parseDate("03/06/82", "dd/MM/yy"));
persona.put("soltero", false);
persona.put("cantHijos", 0);
Dejemos de lado por un momento el campo "fechaNacimiento" y veamos cómo se representaría este Mapa en formato JSON, repito: sin la fecha de nacimiento:
{
"nombre" : "Adrian",
"apellido" : "Paredes",
"cantHijos" : 0,
"soltero" : false
}
Ahora agreguemos el atributo "fechaNacimiento":
{
"nombre" : "Adrian",
"apellido" : "Paredes",
"cantHijos" : 0,
"soltero" : false,
"fechaNacimiento" : {
"year" : 82
"month" : 5,
"day" : 4,
"date" : 3,
"hours" : 0,
"minutes" : 0,
"seconds" : 0,
"time" : 391921200000,
"timezoneOffset" : 180,
}
}
El atributo "fechaNacimiento" es del tipo
java.util.Date, por lo tanto, lo que estamos viendo en el JSON es un Mapa dentro del Mapa principal que es Persona. Los campos "year", "month", "day", "date", "hours", "minutes", "seconds", "time" y "timezoneOffset" son los atributos de la clase
java.util.Date.
Hay algunas bibliotecas de JSON que serializan los objetos
java.util.Date de formas más compacta. Estas formas escapan al estándar de JSON. Por ejemplo,
jabsorb, un broker que sirve como framework liviano para aplicaciones Ajax/Web 2.0, escribiría el JSON de Persona de esta manera:
{
"nombre" : "Adrian",
"apellido" : "Paredes",
"cantHijos" : 0,
"soltero" : false,
"fechaNacimiento" : {
"javaClass" : "java.util.Date",
"time" : 391921200000
}
}
Como
jabsorb es un broker diseñado para transportar datos a tavés de una red, esta forma de serializar los objetos Date es beneficiosa, ya que suprime la información redundante.
Para hacer un poco más interesante esta introducción a JSON, vamos a agregarle dos atributos a la clase Persona:
private Direccion direccion;
private List nombreHijos;
// Y agregamos los setters y getters...
La clase
Direccion está definida de esta forma:
public class Direccion {
private String calle;
private Integer numero;
// ... Los setters y getters para cada atributo ...
}Supongamos que la Persona "Adrián Paredes" esta vez sí tiene hijos y tiene tres: "Marco", "Polo" y "Lolita":
Persona persona = new Persona();
persona.setNombre("Adrian");
persona.setApellido("Paredes");
persona.setFechaNacimiento(parseDate("03/06/82", "dd/MM/yy"));
persona.setSoltero(false);
persona.setCantHijos(3);
List nombres = new ArrayList();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.setNombreHijos(nombres);
Direccion direccion = new Direccion();
direccion.setCalle("Av. Siempre Viva");
direccion.setNumero(2012);
persona.setDireccion(direccion);
Lo que en el lenguaje de Mapas y Listas sería:
Map persona = new HashMap();
persona.put("nombre", "Adrian");
persona.put("apellido", "Paredes");
persona.put("fechaNacimiento", parseDate("03/06/82", "dd/MM/yy"));
persona.put("soltero", false);
persona.put("cantHijos", 3);
List nombres = new ArrayList();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.put("nombreHijos", nombres);
Map direccion = new HashMap();
direccion.put("calle", "Av. Siempre Viva");
direccion.put("numero", 2012);
persona.put("direccion", direccion);
Serializado a JSON este objeto quedaría:
{
"nombre" : "Adrian",
"apellido" : "Paredes",
"fechaNacimiento" : {
"year" : 82
"month" : 5,
"day" : 4,
"date" : 3,
"hours" : 0,
"minutes" : 0,
"seconds" : 0,
"time" : 391921200000,
"timezoneOffset" : 180,
},
"cantHijos" : 3,
"nombreHijos" : ["Marco", "Polo", "Lolita"],
"soltero" : false,
"direccion" : {
"calle" : "Av. Siempre Viva",
"numero" : 2012
}
}
JSONLint (Validador de JSON)
JSONLint es un validador de JSON on-line. Muy útil para cuando queremos verificar si un JSON está bien formado o no. Cuando un JSON es muy grande, la población de llaves y corchetes puede crecer bastante. Si nos olvidamos algún corchete de cierre, alguna llave de apertura, algunas comillas, JSONLint nos indicará en qué línea está el problema, simplemente copiando y pegando la cadena JSON y presionando el botón
Validate.
JSON-lib
Lo más práctico de JSON es que es muy fácil de parsear. Al final de la
página oficial de JSON hay una lista de bibliotecas útiles para usar en distintos lenguajes (Java, C#, Delphi, Lisp, Phyton, Ruby, etc). Siendo Java tan popular, hay muchas herramientas que sirven para este lenguaje. Una de ellas es
JSON-lib.
JSON-lib es una poderosa biblioteca que permite transformar Java Beans, Mapas, Colecciones, Arrays y XML a JSON y viceversa. Hasta convierte de JSON a DynaBeans de Apache Commons, si no necesitamos crear una clase. También soporta algunas funcionalidades que no entran dentro del estándar como la serialización y deserialización de funciones JavaScript.
Usar esta librería es muy fácil. Si estás usando Maven2, la dependencia a incluir es:
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.3</version>
<classifier>jdk15</classifier>
<dependency>
Sino, debes incluir en el classpath estas librerías:
- jakarta commons-lang 2.4
- jakarta commons-beanutils 1.7.0
- jakarta commons-collections 3.2
- jakarta commons-logging 1.1.1
- ezmorph 1.0.6
Además del mismo jar de JSON-lib, por supuesto.
A la hora de parsear una cadena JSON, JSON-lib lo que hace es almacenar los Objetos y los Arrays en Mapas y Listas. Un objeto JSON es volcado en una instancia de la clase
JSONObject y un array JSON a una instancia de la clase
JSONArray. Ambas clases implementan las interfaces
java.util.Map y
java.util.List respectivamente.
Pasar de Java Bean, Mapa o Lista a JSONObject es casi trivial. Simplemente usamos el método estático de
fromObject de
JSONObject o
JSONArray según corresponda.
Por ejemplo, si queremos pasar un objeto
Persona a
JSON:
Persona persona = new Persona();
persona.setNombre("Adrian");
persona.setApellido("Paredes");
persona.setFechaNacimiento(parseDate("03/06/82", "dd/MM/yy"));
persona.setSoltero(false);
persona.setCantHijos(3);
List nombres = new ArrayList();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.setNombreHijos(nombres);
Direccion direccion = new Direccion();
direccion.setCalle("Av. Siempre Viva");
direccion.setNumero(2012);
persona.setDireccion(direccion);
JSONObject jsonObj = JSONObject.fromObject(persona);
System.out.println(jsonObj);
La salida del
System.out.println de este código va a ser sencillamente el string JSON:
{
"nombre" : "Adrian",
"apellido" : "Paredes",
"fechaNacimiento" : {
"year" : 82
"month" : 5,
"day" : 4,
"date" : 3,
"hours" : 0,
"minutes" : 0,
"seconds" : 0,
"time" : 391921200000,
"timezoneOffset" : 180,
},
"cantHijos" : 3,
"nombreHijos" : ["Marco", "Polo", "Lolita"],
"soltero" : false,
"direccion" : {
"calle" : "Av. Siempre Viva",
"numero" : 2012
}
}Esto es así, ya que el objeto
JSONObject tiene sobrecargado el
toString() para que escupa el mapa en formato JSON. Así de sencillo.
Si queremos pasar de
HashMap a
JSON, el código es exactamente igual:
Map persona = new HashMap();
persona.put("nombre", "Adrian");
persona.put("apellido", "Paredes");
persona.put("fechaNacimiento", parseDate("03/06/82", "dd/MM/yy"));
persona.put("soltero", false);
persona.put("cantHijos", 3);
List nombres = new ArrayList();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.put("nombreHijos", nombres);
Map direccion = new HashMap();
direccion.put("calle", "Av. Siempre Viva");
direccion.put("numero", 2012);
persona.put("direccion", direccion);
JSONObject jsonObj = JSONObject.fromObject(persona);
System.out.println(jsonObj);
El
fromObject puede recibir prácticamente cualquier cosa. Ya vimos que podemos mandarle un Bean y un HashMap, cualquier cosa que pueda convertirse en Mapa de JSON. Por supuesto, también puede recibir un String JSON. De esta forma, podemos deserealizar objetos:
String jsonStringPersona = "{" +
"\"apellido\":\"Paredes\"," +
"\"nombre\":\"Adrian\"," +
"\"direccion\":{" +
"\"calle\":\"Av. Siempre Viva\"," +
"\"numero\":2012" +
"}," +
"\"soltero\":false," +
"\"nombreHijos\":[\"Marco\",\"Polo\",\"Lolita\"]," +
"\"cantHijos\":3," +
"\"fechaNacimiento\":{" +
"\"date\":3," +
"\"day\":4," +
"\"hours\":0," +
"\"minutes\":0," +
"\"month\":5," +
"\"seconds\":0," +
"\"time\":391921200000," +
"\"timezoneOffset\":180," +
"\"year\":82" +
"}" +
"}";
JSONObject jsonPersona = JSONObject.fromObject(jsonStringPersona);
String nombre = jsonPersona.getString("nombre");
String apellido = jsonPersona.getString("apellido");
JSONObject jsonDireccion = jsonPersona.getJSONObject("direccion");
String calle = jsonDireccion.getString("calle");
Integer numero = jsonDireccion.getInt("numero");
JSONArray jsonArray = jsonPersona.getJSONArray("nombreHijos");
String hijo1 = jsonArray.getString(0);
String hijo2 = jsonArray.getString(1);
String hijo3 = jsonArray.getString(2);
System.out.println(nombre + " " + apellido);
System.out.println("Vive en: " + calle + " " + numero);
System.out.println("Sus hijos se llaman: " + hijo1 + ", " + hijo2 + " y " + hijo3);
Recordemos que un
JSONObject implementa la interfaz
java.util.Map, por lo que al levantar el string JSON en un objeto
JSONObject ya tenemos un Mapa con el que podemos manipular los datos.
Si quisiéramos volcar el
JSONObject en el Java Bean
Persona podríamos hacer:
Persona persona = (Persona) JSONObject.toBean(jsonPersona, Persona.class);
Si miramos lo que tiene
persona, vemos que la deserealización se ha realizado con éxito:
System.out.println(ReflectionToStringBuilder.toString(persona));
Otra funcionalidad muy interesante de JSON-lib es que permite pasar de JSON a XML y de XML a JSON. Por ejemplo, si quisiéramos serializar el
jsonPersona a XML, tendríamos que hacer:
XMLSerializer xmlSerializer = new XMLSerializer();
String xml = xmlSerializer.write(jsonPersona);
System.out.println(xml);
Para pasar de XML a JSON es un poco más complicado (tampoco tanto). A quien le interese explorar esta funcionalidad, la documentación de la biblioteca nos recomienda
este tutorial.
Cabe aclarar que para que funcione el
XMLSerializer en tiempo de ejecución se necesita una dependencia no mencionada en la documentación y no incluida en el pom.xml de JSON-lib:
<dependency>
<groupId>xom</groupId>
<artifactId>xom</artifactId>
<version>1.1</version>
</dependency>
JSON-RPC
JSON-RPC es un protocolo de llamadas a procedimientos remotos similar a
XML-RPC, pero más liviano. Como dice la especificación,
fue diseñado para ser simple. Se trata de un protocolo para un mecanismo típico de RPC en donde hay un cliente y un servidor que establecen una conexión. El cliente invoca métodos remotos publicados por el servidor enviando un
request con el nombre del método y sus parámetros. El servidor contesta con un
response que contiene los datos de respuesta. Tanto los request como los response son paquetes HTTP cuyo recurso (información transmitida) son objetos JSON.
El objeto request contiene tres propiedades:
- method: un String que contiene el nombre del método a invocar
- params: una Lista de Objetos para pasar como argumento del método
- id: un identificador que se usa para matchear el request con el response
El objeto response contiene tres propiedades:
- result: el Objeto resultado que retorna la invocación del método (debe ser nulo si hubo un error en la invocación)
- error: un Objeto error que será nulo si no hubo error en la invocación del método
- id: el mismo identificador del request
Las
notificaciones son peticiones especiales que no reciben una respuesta. El objeto de notificación tiene las mismas dos primeras propiedades que un objeto request (
method y
params) y la propiedad
id debe ser nula.
Un ejemplo de request podría ser:
{ "method" : "echo", "params" : ["Hello JSON-RPC"], "id" : 1 }
Lo que equivaldría a la invocación remota:
service.echo("Hello JSON-RPC")Un ejemplo de response sin error para esta invocación podría ser:
{ "result" : "Hello JSON-RPC", "error" : null, "id" : 1 }
Un response con error:
{ "error" : { "message" : "Se ha producido un error de comunicación.", "code" : 15}, "id" : 1 }
jabsorb (Java to JavaScript Object Request Broker)
jabsorb es un framework Ajax/Web 2.0 simple y liviano. Permite realizar llamadas a métodos de objetos Java alojados en un servidor de aplicaciones web desde código JavaScript de forma transparente, como si el código de los objetos residiera en el browser cliente. Encapsula por completo la serialización y deserealización de los objetos y usa como protocolo de transporte el recién mencionado JSON-RPC.
Lamentablemente parece que la gente que desarrolla este broker nunca subió una distribución al repositorio público de Maven. De todas formas, yo lo he encontrado para bajar en un repositorio de Apache exclusivo del proyecto
Apache Tuscany. Esto sirve para "zafar", pero bajo ningún punto de vista es una solución buena, porque dependemos de que la gente de Apache Tuscany mantenga este servidor y de que si aparece una nueva versión la actualicen. Por otra parte, no veo a la gente de jabsorb con intención de subir la biblioteca a un repositorio de Maven en un futuro cercano, considerando que esto está reportado como bug de prioridad media y continúa abierto desde el 16 de enero de 2008 (
Ver Bug #40). Así que, para los que usamos Maven, tenemos que conformarnos con descargar la dependencia de algún repositorio de un proyecto público que encontremos o bajar el jar desde la página de jabsorb, a la antigua, y subirlo a un repositorio local que tengamos en nuestra empresa o usarlo directamente del repositorio local de nuestra máquina.
Si quieren descargar la dependencia desde el repositorio del proyecto Apache Tuscany, pueden agregar el servidor en el
settings.xml de Maven:
<repository>
<id>tuscany.apache.org</id>
<name>Apache Tuscany</name>
<url>http://svn.apache.org/repos/asf/tuscany/maven</url>
<layout>default</layout>
</repository>
Y luego agregar la siguiente dependencia en
pom.xml del proyecto:
<dependency>
<groupId>org.jabsorb</groupId>
<artifactId>jabsorb</artifactId>
<version>1.3.1</version>
</dependency>
Veamos un ejemplo de uso muy sencillo, así entendemos la potencia de este mecanismo de RPC.Dado que nuestro objetivo es comunicar código JavaScript que corre del lado del cliente con código Java que corre del lado del servidor, para probar jabsorb tendremos que armar una aplicación web. Si desean crear un proyecto de aplicación web para Eclipse y Tomcat usando un arquetipo de Maven2 y no saben cómo, pueden consultar los post
HelloServlet Web Application con Maven2 y
Eclipse y Plugin de Maven2 para Sysdeo de este mismo blog.
La biblioteca expone sólo dos componentes:
JSONRPCBridge y
JSONRPCServlet. Para registrar el
JSONRPCServlet, quien maneja las peticiones HTTP, tendremos que agregar el mapeo en el
web.xml de la aplicación:
<servlet>
<servlet-name>JSONRPCServlet</servlet-name>
<servlet-class>org.jabsorb.JSONRPCServlet</servlet-class>
<init-param>
<param-name>gzip_threshold</param-name>
<param-value>200</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>JSONRPCServlet</servlet-name>
<url-pattern>/JSON-RPC</url-pattern>
</servlet-mapping>
El
JSONRPCBridge mantiene las referencias a los
objetos exportados, los objetos a los que se le pueden ejecutar invocaciones remotas. Tiene como responsabilidad decodificar las llamadas a los métodos, localizar el objeto y realizar la ejecución. Para que una única instancia de
JSONRPCBridge esté disponible durante la conexión entre el cliente y el servidor, debemos crearla y guardarla en la sesión del servidor.
Por ejemplo, si estamos usando JSP para escribir los servlets podemos hacer:
<jsp:useBean id="JSONRPCBridge" scope="session" class="org.jabsorb.JSONRPCBridge" />
Luego, creamos una clase Java bien simple con el código que queremos ejecutar desde JavaScript. Por ejemplo:
package com.blogspot.tecnologiasjava.jarsorb.pruebas;
public class Hello {
public String sayHello(String who) {
return "hello " + who;
}
}
Luego tendremos que registrar una instancia de la clase
Hello en el bridge:
<% JSONRPCBridge.registerObject("hello", hello); %>
Listo. De esta forma tan simple hemos publicado todos los métodos públicos de la clase
Hello para que puedan ser ejecutados de forma remota desde un navegador.
Veamos ahora el código JavaScript necesario para el lado del cliente:
var jsonrpc;
function onLoad() {
jsonrpc = new JSONRpcClient("/JSON-RPC");
}
function clickHello() {
var whoNode = document.getElementById("who");
var result = jsonrpc.hello.sayHello(whoNode.value);
alert("The server replied: " + result);
}
Veamos el jsp
hello.jsp completo:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<jsp:useBean id="JSONRPCBridge" scope="session"
class="org.jabsorb.JSONRPCBridge"/>
<jsp:useBean id="hello" scope="session"
class="com.blogspot.tecnologiasjava.jarbsorb.pruebas.Hello"/>
<%JSONRPCBridge.registerObject("hello", hello);%>
<html>
<head>
<script type="text/javascript" src="jsonrpc.js"></script>
<script type="text/javascript" src="hello.js"></script>
<title>jabsorb Hello</title>
</head>
<body bgcolor="#ffffff" onLoad="onLoad()">
<h2>jabsorb Hello</h2>
<p>The jabsorb <em>Hello World</em> application.</p>
<p>
<strong>Who:</strong>
<input type="text" id="who" size="30" value="Adrian"/>
<input type="button" value="Say Hello" onclick="clickHello()"/>
</p>
</body>
</html>
Si queremos ejecutar este ejemplo, debemos bajarnos el ZIP de la
página de descarga de jabsorb. Este ejemplo de
Hello viene dentro. Pero no sólo por eso es importante bajarlo, además necesitamos el cliente JSON-RPC de jabsorb escrito en JavaScript, el archivo
jsonrpc.js, que podemos copiar de la carpeta
webapps/jsonrpc directo a la carpeta
webapp de nuestro proyecto.
Con los archivos
hello.js,
hello.jsp y
jsonrpc.js en nuestra carpeta
webapp y la clase
Hello.java en el paquete
com.blogspot.tecnologiasjava.jarbsorb.pruebas, estamos listos para levantar el Tomcat (o el servidor de aplicaciones web que tengamos) y, si todo salió bien, cuando hagamos click en el botón de
Say Hello del formulario de prueba de
http://localhost:8080/jabsorb_pruebas/hello.jsp, el alert nos saludará con el texto
The server replied: hello Adrian, o el nombre que hayamos escrito en el input text.

La línea más importante para analizar en el código es ésta:
var result = jsonrpc.hello.sayHello(whoNode.value);
Aquí es donde toda la magia de
jabsorb y
JSON-RPC muestra su esplendor. Una línea de código JavaScript que ejecuta un método de una clase Java ubicada en el servidor. Los paquetes JSON-RPC van por debajo, son invisibles para el programador.
Si nos ponemos a pensar un poco, este mecanismo de RPC transparente que proporciona
jabsorb tiene bastantes similitudes con los mecanismos de RPC que proveen otros frameworks bastante más pesados, como por ejemplo
EJB3. Claro que EJB3 es una plataforma y provee muchas más funcionalidades que obviamente JSON-RPC no provee, ni intenta proveer, pero lo asombroso es que, con tan poco y con tanta simpleza, las funcionalidades provistas por
jabsorb y otras tecnologías livianas basadas en JSON resultan muy poderosas.
Referencias