1. Información general
Este artículo le mostrará cómo procesar JSON utilizando solo Java EE central, sin el uso de dependencias de terceros como Jersey o Jackson. Prácticamente todo lo que usaremos lo proporciona el paquete javax.json.
2. Escribir un objeto en una cadena JSON
Convertir un objeto Java en una cadena JSON es muy fácil. Supongamos que tenemos una clase Person simple :
public class Person { private String firstName; private String lastName; private Date birthdate; // getters and setters }
Para convertir una instancia de esa clase en una cadena JSON , primero necesitamos crear una instancia de JsonObjectBuilder y agregar pares de propiedad / valor usando el método add () :
JsonObjectBuilder objectBuilder = Json.createObjectBuilder() .add("firstName", person.getFirstName()) .add("lastName", person.getLastName()) .add("birthdate", new SimpleDateFormat("DD/MM/YYYY") .format(person.getBirthdate()));
Observe que el método add () tiene algunas versiones sobrecargadas. Puede recibir la mayoría de los tipos primitivos (así como objetos en caja) como segundo parámetro.
Una vez que terminemos de configurar las propiedades, solo necesitamos escribir el objeto en una cadena :
JsonObject jsonObject = objectBuilder.build(); String jsonString; try(Writer writer = new StringWriter()) { Json.createWriter(writer).write(jsonObject); jsonString = writer.toString(); }
¡Y eso es! La cadena generada se verá así:
{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978"}
2.1. Usando JsonArrayBuilder para construir matrices
Ahora, para agregar un poco más de complejidad a nuestro ejemplo, supongamos que la clase Person se modificó para agregar una nueva propiedad llamada correos electrónicos que contendrá una lista de direcciones de correo electrónico:
public class Person { private String firstName; private String lastName; private Date birthdate; private List emails; // getters and setters }
Para agregar todos los valores de esa lista al JsonObjectBuilder , necesitaremos la ayuda de JsonArrayBuilder :
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); for(String email : person.getEmails()) { arrayBuilder.add(email); } objectBuilder.add("emails", arrayBuilder);
Observe que estamos usando otra versión sobrecargada del método add () que toma un objeto JsonArrayBuilder como segundo parámetro.
Entonces, veamos la Cadena generada para un objeto Persona con dos direcciones de correo electrónico:
{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978", "emails":["[email protected]","[email protected]"]}
2.2. Formatear la salida con PRETTY_PRINTING
Así que hemos convertido con éxito un objeto Java en una cadena JSON válida . Ahora, antes de pasar a la siguiente sección, agreguemos un formato simple para que la salida sea más "similar a JSON" y más fácil de leer.
En los ejemplos anteriores, creamos un JsonWriter utilizando el sencillo Json . método estático createWriter () . Para tener un mayor control de la cadena generada , aprovecharemos la capacidad JsonWriterFactory de Java 7 para crear un escritor con una configuración específica.
Map config = new HashMap(); config.put(JsonGenerator.PRETTY_PRINTING, true); JsonWriterFactory writerFactory = Json.createWriterFactory(config); String jsonString; try(Writer writer = new StringWriter()) { writerFactory.createWriter(writer).write(jsonObject); jsonString = writer.toString(); }
El código puede parecer un poco detallado, pero realmente no hace mucho.
Primero, crea una instancia de JsonWriterFactory pasando un mapa de configuración a su constructor. El mapa contiene solo una entrada que establece como verdadero a la propiedad PRETTY_PRINTING. Luego, usamos esa instancia de fábrica para crear un escritor, en lugar de usar Json.createWriter () .
La nueva salida contendrá los saltos de línea distintivos y la tabulación que caracteriza a una cadena JSON :
{ "firstName":"Michael", "lastName":"Scott", "birthdate":"06/15/1978", "emails":[ "[email protected]", "[email protected]" ] }
3. Creación de un objeto Java a partir de una cadena
Ahora hagamos la operación opuesta: convierta una cadena JSON en un objeto Java.
La parte principal del proceso de conversión gira en torno a JsonObject . Para crear una instancia de esta clase, use el método estático Json.createReader () seguido de readObject () :
JsonReader reader = Json.createReader(new StringReader(jsonString)); JsonObject jsonObject = reader.readObject();
El método createReader () toma un InputStream como parámetro. En este ejemplo, estamos usando un StringReader, ya que nuestro JSON está contenido en un objeto String , pero este mismo método podría usarse para leer contenido de un archivo, por ejemplo, usando FileInputStream .
Con una instancia de JsonObject a mano, podemos leer las propiedades usando el método getString () y asignar los valores obtenidos a una instancia recién creada de nuestra clase Person :
Person person = new Person(); person.setFirstName(jsonObject.getString("firstName")); person.setLastName(jsonObject.getString("lastName")); person.setBirthdate(dateFormat.parse(jsonObject.getString("birthdate")));
3.1. Usando JsonArray para obtener valores de lista
Necesitaremos usar una clase especial, llamada JsonArray para extraer valores de lista de JsonObject :
JsonArray emailsJson = jsonObject.getJsonArray("emails"); List emails = new ArrayList(); for (JsonString j : emailsJson.getValuesAs(JsonString.class)) { emails.add(j.getString()); } person.setEmails(emails);
¡Eso es! Hemos creado una instancia completa de Person a partir de una cadena Json .
4. Consulta de valores
Ahora, supongamos que estamos interesados en un dato muy específico que se encuentra dentro de una cadena JSON .
Considere el JSON a continuación que representa a un cliente de una tienda de mascotas. Digamos que, por alguna razón, necesita obtener el nombre de la tercera mascota de la lista de mascotas:
{ "ownerName": "Robert", "pets": [{ "name": "Kitty", "type": "cat" }, { "name": "Rex", "type": "dog" }, { "name": "Jake", "type": "dog" }] }
Convertir todo el texto en un objeto Java solo para obtener un valor único no sería muy eficiente. Entonces, revisemos un par de estrategias para consultar JSON Strings sin tener que pasar por toda la prueba de conversión.
4.1. Consulta mediante la API del modelo de objetos
Consultar el valor de una propiedad con una ubicación conocida en la estructura JSON es sencillo. Podemos usar una instancia de JsonObject, la misma clase usada en ejemplos anteriores:
JsonReader reader = Json.createReader(new StringReader(jsonString)); JsonObject jsonObject = reader.readObject(); String searchResult = jsonObject .getJsonArray("pets") .getJsonObject(2) .getString("name");
El problema aquí es navegar a través de las propiedades de jsonObject usando la secuencia correcta de métodos get * () .
In this example, we first get a reference to the “pets” list using getJsonArray(), which returns a list with 3 records. Then, we use getJsonObject() method, which takes an index as a parameter, returning another JsonObject representing the third item in the list. Finally, we use getString() to get the string value we are looking for.
4.2. Querying Using Streaming API
Another way to perform precise queries on a JSON String is using the Streaming API, which has JsonParser as its main class.
JsonParser provides extremely fast, read-only, forward access to JS, with the drawback of being somewhat more complicated than the Object Model:
JsonParser jsonParser = Json.createParser(new StringReader(jsonString)); int count = 0; String result = null; while(jsonParser.hasNext()) { Event e = jsonParser.next(); if (e == Event.KEY_NAME) { if(jsonParser.getString().equals("name")) { jsonParser.next(); if(++count == 3) { result = jsonParser.getString(); break; } } } }
This example delivers the same result as the previous one. It returns the name from the third pet in the pets list.
Once a JsonParser is created using Json.createParser(), we need to use an iterator (hence the “forward access” nature of the JsonParser) to navigate through the JSON tokens until we get to the property (or properties) we are looking for.
Every time we step through the iterator we move to the next token of the JSON data. So we have to be careful to check if the current token has the expected type. This is done by checking the Event returned by the next() call.
There are many different types of tokens. In this example, we are interested in the KEY_NAME types, which represent the name of a property (e.g. “ownerName”, “pets”, “name”, “type”). Once we stepped through a KEY_NAME token with a value of “name” for the third time, we know that the next token will contain a string value representing the name of the third pet from the list.
This is definitely harder than using the Object Model API, especially for more complicated JSON structures. The choice between one or the other, as always, depends on the specific scenario you will be dealing with.
5. Conclusion
We have covered a lot of ground on the Java EE JSON Processing API with a couple of simple examples. To learn other cool stuff about JSON processing, check our series of Jackson articles.
Consulta el código fuente de las clases utilizadas en este artículo, así como algunas pruebas unitarias, en nuestro repositorio de GitHub.