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
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:
Mappersona = 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 ListnombreHijos;
// 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);
Listnombres = 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:
Mappersona = 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);
Listnombres = new ArrayList ();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.put("nombreHijos", nombres);
Mapdireccion = 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
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);
Listnombres = 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:
Mappersona = 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);
Listnombres = new ArrayList ();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.put("nombreHijos", nombres);
Mapdireccion = 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
- 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
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.
1 comentarios:
este blog esta muy bueno y explicativo soy desarrollador j2ee y si en algún momento quieren que colabore con algo trabajo con tecnología java(jdeveloper) sobre oracle
Publicar un comentario en la entrada