Elaborazione JSON in Java EE 7

1. Panoramica

Questo articolo ti mostrerà come elaborare JSON utilizzando solo Java EE di base, senza l'uso di dipendenze di terze parti come Jersey o Jackson. Praticamente tutto ciò che useremo è fornito dal pacchetto javax.json.

2. Scrittura di un oggetto in una stringa JSON

Convertire un oggetto Java in una stringa JSON è semplicissimo. Supponiamo di avere una semplice classe Person :

public class Person { private String firstName; private String lastName; private Date birthdate; // getters and setters }

Per convertire un'istanza di quella classe in una stringa JSON , prima dobbiamo creare un'istanza di JsonObjectBuilder e aggiungere coppie proprietà / valore utilizzando il metodo add () :

JsonObjectBuilder objectBuilder = Json.createObjectBuilder() .add("firstName", person.getFirstName()) .add("lastName", person.getLastName()) .add("birthdate", new SimpleDateFormat("DD/MM/YYYY") .format(person.getBirthdate()));

Si noti che il metodo add () ha alcune versioni sovraccaricate. Può ricevere la maggior parte dei tipi primitivi (così come gli oggetti in scatola) come secondo parametro.

Una volta che abbiamo finito di impostare le proprietà, dobbiamo solo scrivere l'oggetto in una stringa :

JsonObject jsonObject = objectBuilder.build(); String jsonString; try(Writer writer = new StringWriter()) { Json.createWriter(writer).write(jsonObject); jsonString = writer.toString(); }

E questo è tutto! La stringa generata sarà simile a questa:

{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978"}

2.1. Utilizzo di JsonArrayBuilder per creare array

Ora, per aggiungere un po 'più di complessità al nostro esempio, supponiamo che la classe Person sia stata modificata per aggiungere una nuova proprietà chiamata emails che conterrà un elenco di indirizzi email:

public class Person { private String firstName; private String lastName; private Date birthdate; private List emails; // getters and setters }

Per aggiungere tutti i valori da quell'elenco a JsonObjectBuilder avremo bisogno dell'aiuto di JsonArrayBuilder :

JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); for(String email : person.getEmails()) { arrayBuilder.add(email); } objectBuilder.add("emails", arrayBuilder);

Si noti che stiamo usando ancora un'altra versione sovraccaricata del metodo add () che accetta un oggetto JsonArrayBuilder come secondo parametro.

Quindi, diamo un'occhiata alla stringa generata per un oggetto Person con due indirizzi email:

{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978", "emails":["[email protected]","[email protected]"]}

2.2. Formattazione dell'output con PRETTY_PRINTING

Quindi abbiamo convertito con successo un oggetto Java in una stringa JSON valida . Ora, prima di passare alla sezione successiva, aggiungiamo una semplice formattazione per rendere l'output più "simile a JSON" e più facile da leggere.

Negli esempi precedenti, abbiamo creato un JsonWriter utilizzando il semplice Json . createWriter () metodo statico. Per ottenere un maggiore controllo sulla stringa generata , sfrutteremo la capacità di JsonWriterFactory di Java 7 per creare un writer con una configurazione specifica.

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(); }

Il codice può sembrare un po 'prolisso, ma in realtà non fa molto.

Innanzitutto, crea un'istanza di JsonWriterFactory passando una mappa di configurazione al suo costruttore. La mappa contiene una sola voce che imposta true sulla proprietà PRETTY_PRINTING. Quindi, usiamo quell'istanza di fabbrica per creare un writer, invece di usare Json.createWriter () .

Il nuovo output conterrà le interruzioni di riga distintive e la tabulazione che caratterizzano una stringa JSON :

{ "firstName":"Michael", "lastName":"Scott", "birthdate":"06/15/1978", "emails":[ "[email protected]", "[email protected]" ] }

3. Creazione di un oggetto Java da una stringa

Ora facciamo l'operazione opposta: converti una stringa JSON in un oggetto Java.

La parte principale del processo di conversione ruota attorno a JsonObject . Per creare un'istanza di questa classe, utilizza il metodo statico Json.createReader () seguito da readObject () :

JsonReader reader = Json.createReader(new StringReader(jsonString)); JsonObject jsonObject = reader.readObject();

Il metodo createReader () accetta un InputStream come parametro. In questo esempio, stiamo utilizzando un StringReader, poiché il nostro JSON è contenuto in un oggetto String , ma questo stesso metodo potrebbe essere utilizzato per leggere il contenuto da un file, ad esempio, utilizzando FileInputStream .

Con un'istanza di JsonObject a portata di mano, possiamo leggere le proprietà utilizzando il metodo getString () e assegnare i valori ottenuti a un'istanza appena creata della nostra classe Person :

Person person = new Person(); person.setFirstName(jsonObject.getString("firstName")); person.setLastName(jsonObject.getString("lastName")); person.setBirthdate(dateFormat.parse(jsonObject.getString("birthdate")));

3.1. Utilizzo di JsonArray per ottenere i valori dell'elenco

We'll need to use a special class, called JsonArray to extract list values from JsonObject:

JsonArray emailsJson = jsonObject.getJsonArray("emails"); List emails = new ArrayList(); for (JsonString j : emailsJson.getValuesAs(JsonString.class)) { emails.add(j.getString()); } person.setEmails(emails);

That's it! We have created a complete instance of Person from a Json String.

4. Querying for Values

Now, let's assume we are interested in a very specific piece of data that lies inside a JSON String.

Consider the JSON below representing a client from a pet shop. Let's say that, for some reason, you need to get the name of the third pet from the pets list:

{ "ownerName": "Robert", "pets": [{ "name": "Kitty", "type": "cat" }, { "name": "Rex", "type": "dog" }, { "name": "Jake", "type": "dog" }] }

Converting the whole text into a Java object just to get a single value wouldn't be very efficient. So, let's check a couple of strategies to query JSON Strings without having to go through the whole conversion ordeal.

4.1. Querying Using Object Model API

Querying for a property's value with a known location in the JSON structure is straightforward. We can use an instance of JsonObject, the same class used in previous examples:

JsonReader reader = Json.createReader(new StringReader(jsonString)); JsonObject jsonObject = reader.readObject(); String searchResult = jsonObject .getJsonArray("pets") .getJsonObject(2) .getString("name"); 

The catch here is to navigate through jsonObject properties using the correct sequence of get*() methods.

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.

Controlla il codice sorgente delle classi utilizzate in questo articolo, così come alcuni unit test, nel nostro repository GitHub.