Esempio di tutorial sull’API del parser Jackson JSON in Java

Jackson JSON Java Parser è molto popolare e utilizzato anche nel framework Spring. Java JSON Processing API non è molto user-friendly e non fornisce funzionalità per la trasformazione automatica da Json a oggetto Java e viceversa. Fortunatamente abbiamo alcune API alternative che possiamo utilizzare per l’elaborazione JSON. Nell’ultimo articolo abbiamo appreso riguardo Google Gson API e abbiamo visto quanto sia facile da usare.

Jackson JSON Java Parser

Per utilizzare l’API Jackson JSON Java nel nostro progetto, possiamo aggiungerla al percorso di compilazione del progetto oppure, se stai usando Maven, possiamo aggiungere la dipendenza seguente.

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.2.3</version>
</dependency>

Il file jackson-databind dipende dalle librerie jackson-core e jackson-annotations, quindi se le aggiungi direttamente al percorso di compilazione, assicurati di aggiungere tutte e tre altrimenti otterrai un errore in fase di esecuzione. L’API di parsing JSON di Jackson fornisce un modo semplice per convertire JSON in un oggetto POJO e supporta una facile conversione in Map da dati JSON. Jackson supporta anche i generics e li converte direttamente da JSON a oggetto.

Esempio di JSON di Jackson

Per il nostro esempio di conversione da JSON a POJO/oggetto Java, prenderemo un esempio complesso con oggetti e array nidificati. Utilizzeremo array, liste e Map negli oggetti Java per la conversione. Il nostro JSON complesso è memorizzato in un file employee.txt con la seguente struttura:

{
  "id": 123,
  "name": "Pankaj",
  "permanent": true,
  "address": {
    "street": "Albany Dr",
    "city": "San Jose",
    "zipcode": 95129
  },
  "phoneNumbers": [
    123456,
    987654
  ],
  "role": "Manager",
  "cities": [
    "Los Angeles",
    "New York"
  ],
  "properties": {
    "age": "29 years",
    "salary": "1000 USD"
  }
}

Abbiamo le seguenti classi Java corrispondenti ai dati JSON.

package com.journaldev.jackson.model;

public class Address {
	
	private String street;
	private String city;
	private int zipcode;
	
	public String getStreet() {
		return street;
	}
	public void setStreet(String street) {
		this.street = street;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public int getZipcode() {
		return zipcode;
	}
	public void setZipcode(int zipcode) {
		this.zipcode = zipcode;
	}
	
	@Override
	public String toString(){
		return getStreet() + ", "+getCity()+", "+getZipcode();
	}
}

La classe Address corrisponde all’oggetto interno nei dati JSON principali.

package com.journaldev.jackson.model;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class Employee {

	private int id;
	private String name;
	private boolean permanent;
	private Address address;
	private long[] phoneNumbers;
	private String role;
	private List<String> cities;
	private Map<String, String> properties;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public boolean isPermanent() {
		return permanent;
	}
	public void setPermanent(boolean permanent) {
		this.permanent = permanent;
	}
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	public long[] getPhoneNumbers() {
		return phoneNumbers;
	}
	public void setPhoneNumbers(long[] phoneNumbers) {
		this.phoneNumbers = phoneNumbers;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	
	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append("***** Employee Details *****\n");
		sb.append("ID="+getId()+"\n");
		sb.append("Name="+getName()+"\n");
		sb.append("Permanent="+isPermanent()+"\n");
		sb.append("Role="+getRole()+"\n");
		sb.append("Phone Numbers="+Arrays.toString(getPhoneNumbers())+"\n");
		sb.append("Address="+getAddress()+"\n");
		sb.append("Cities="+Arrays.toString(getCities().toArray())+"\n");
		sb.append("Properties="+getProperties()+"\n");
		sb.append("*****************************");
		
		return sb.toString();
	}
	public List<String> getCities() {
		return cities;
	}
	public void setCities(List<String> cities) {
		this.cities = cities;
	}
	public Map<String, String> getProperties() {
		return properties;
	}
	public void setProperties(Map<String, String> properties) {
		this.properties = properties;
	}
}

Employee è il bean Java che rappresenta l’oggetto JSON principale. Ora vediamo come possiamo trasformare JSON in un oggetto Java utilizzando l’API del parser JSON di Jackson.

package com.journaldev.jackson.json;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.journaldev.jackson.model.Address;
import com.journaldev.jackson.model.Employee;


public class JacksonObjectMapperExample {

	public static void main(String[] args) throws IOException {
		
		//leggi i dati del file JSON come stringa
		byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
		
		//crea un'istanza di ObjectMapper
		ObjectMapper objectMapper = new ObjectMapper();
		
		//converti la stringa JSON in un oggetto
		Employee emp = objectMapper.readValue(jsonData, Employee.class);
		
		System.out.println("Employee Object\n"+emp);
		
		//converti l'oggetto in una stringa JSON
		Employee emp1 = createEmployee();
		//configura l'ObjectMapper per la stampa formattata
		objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
		
		//scrittura su console, può essere scritta su qualsiasi flusso di output come un file
		StringWriter stringEmp = new StringWriter();
		objectMapper.writeValue(stringEmp, emp1);
		System.out.println("Employee JSON is\n"+stringEmp);
	}
	
	public static Employee createEmployee() {

		Employee emp = new Employee();
		emp.setId(100);
		emp.setName("David");
		emp.setPermanent(false);
		emp.setPhoneNumbers(new long[] { 123456, 987654 });
		emp.setRole("Manager");

		Address add = new Address();
		add.setCity("Bangalore");
		add.setStreet("BTM 1st Stage");
		add.setZipcode(560100);
		emp.setAddress(add);

		List cities = new ArrayList();
		cities.add("Los Angeles");
		cities.add("New York");
		emp.setCities(cities);

		Map props = new HashMap();
		props.put("salary", "1000 Rs");
		props.put("age", "28 years");
		emp.setProperties(props);

		return emp;
	}

}

Quando eseguiamo il programma sopra, otterrai l’output seguente.

Employee Object
***** Employee Details *****
ID=123
Name=Pankaj
Permanent=true
Role=Manager
Phone Numbers=[123456, 987654]
Address=Albany Dr, San Jose, 95129
Cities=[Los Angeles, New York]
Properties={age=29 years, salary=1000 USD}
*****************************
Employee JSON is
//stampa lo stesso dei dati del file json sopra

com.fasterxml.jackson.databind.ObjectMapper è la classe più importante nell’API di Jackson che fornisce i metodi readValue() e writeValue() per trasformare JSON in oggetto Java e oggetto Java in JSON. La classe ObjectMapper può essere riutilizzata e possiamo inizializzarla una volta come oggetto Singleton. Ci sono così tante versioni sovraccaricate dei metodi readValue() e writeValue() per lavorare con array di byte, File, input/output stream e oggetti Reader/Writer.

Jackson JSON – Conversione di JSON in Mappa

A volte abbiamo un oggetto JSON come segue, nel file data.txt:

{
  "name": "David",
  "role": "Manager",
  "city": "Los Angeles"
}

e vogliamo convertirlo in una mappa e non in un oggetto java con le stesse proprietà e chiavi. Possiamo farlo molto facilmente nell’API di Jackson JSON con due metodi con il codice seguente:

//converting json to Map
byte[] mapData = Files.readAllBytes(Paths.get("data.txt"));
Map myMap = new HashMap();

ObjectMapper objectMapper = new ObjectMapper();
myMap = objectMapper.readValue(mapData, HashMap.class);
System.out.println("Map is: "+myMap);

//another way
myMap = objectMapper.readValue(mapData, new TypeReference>() {});
System.out.println("Map using TypeReference: "+myMap);

Una volta eseguito lo snippet sopra, otteniamo l’output seguente:

Map is: {name=David, role=Manager, city=Los Angeles}
Map using TypeReference: {name=David, role=Manager, city=Los Angeles}

Jackson JSON – Leggere una chiave JSON specifica

A volte abbiamo dati JSON e siamo interessati solo ad alcuni dei valori delle chiavi, quindi in quel caso convertire l’intero JSON in un oggetto non è una buona idea. Jackson JSON API fornisce l’opzione per leggere i dati JSON come un albero simile al DOM Parser e possiamo leggere elementi specifici dell’oggetto JSON attraverso questo. Il codice seguente fornisce uno snippet per leggere voci specifiche da un file JSON.

// leggere i dati del file JSON come stringa
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));

// creare un'istanza di ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();

// leggere JSON come DOM Parser
JsonNode rootNode = objectMapper.readTree(jsonData);
JsonNode idNode = rootNode.path("id");
System.out.println("id = "+idNode.asInt());

JsonNode phoneNosNode = rootNode.path("phoneNumbers");
Iterator elements = phoneNosNode.elements();
while(elements.hasNext()){
	JsonNode phone = elements.next();
	System.out.println("Phone No = "+phone.asLong());
}

Otteniamo il seguente output quando eseguiamo lo snippet di codice sopra.

id = 123
Phone No = 123456
Phone No = 987654

Jackson JSON – Modifica Documento JSON

L’API Java di Jackson JSON fornisce metodi utili per aggiungere, modificare e rimuovere chiavi dai dati JSON e poi possiamo salvarli come nuovo file JSON o scriverli su qualsiasi flusso. Il codice seguente ci mostra come fare questo facilmente.

byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));

ObjectMapper objectMapper = new ObjectMapper();

// creare JsonNode
JsonNode rootNode = objectMapper.readTree(jsonData);

// aggiornare i dati JSON
((ObjectNode) rootNode).put("id", 500);
// aggiungere nuovo valore chiave
((ObjectNode) rootNode).put("test", "test value");
// rimuovere chiave esistente
((ObjectNode) rootNode).remove("role");
((ObjectNode) rootNode).remove("properties");
objectMapper.writeValue(new File("updated_emp.txt"), rootNode);

Se esegui il codice sopra e cerchi il nuovo file, noterai che non ha le chiavi “ruolo” e “proprietà”. Noterai anche che il valore “id” è stato aggiornato a 500 e una nuova chiave “test” è stata aggiunta al file updated_emp.txt.

Esempio di utilizzo dell’API di Jackson JSON Streaming

L’API di Jackson JSON Java fornisce anche il supporto per lo streaming che è utile nel lavoro con grandi dati JSON perché legge l’intero file come token e utilizza meno memoria. L’unico problema con l’API di streaming è che dobbiamo occuparci di tutti i token durante il parsing dei dati JSON. Se abbiamo dati JSON come {“ruolo”:“Manager”} allora otterremo i seguenti token in ordine – { (inizio oggetto), “ruolo” (nome chiave), “Manager” (valore chiave) e } (fine oggetto). Due punti (:) sono il delimitatore in JSON e quindi non considerati come un token.

package com.journaldev.jackson.json;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.journaldev.jackson.model.Address;
import com.journaldev.jackson.model.Employee;

public class JacksonStreamingReadExample {

	public static void main(String[] args) throws JsonParseException, IOException {
		
		//crea l'oggetto JsonParser
		JsonParser jsonParser = new JsonFactory().createParser(new File("employee.txt"));
		
		//scorre i token
		Employee emp = new Employee();
		Address address = new Address();
		emp.setAddress(address);
		emp.setCities(new ArrayList());
		emp.setProperties(new HashMap());
		List phoneNums = new ArrayList();
		boolean insidePropertiesObj=false;
		
		parseJSON(jsonParser, emp, phoneNums, insidePropertiesObj);
		
		long[] nums = new long[phoneNums.size()];
		int index = 0;
		for(Long l :phoneNums){
			nums[index++] = l;
		}
		emp.setPhoneNumbers(nums);
		
		jsonParser.close();
		//stampa l'oggetto impiegato
		System.out.println("Employee Object\n\n"+emp);
	}

	private static void parseJSON(JsonParser jsonParser, Employee emp,
			List phoneNums, boolean insidePropertiesObj) throws JsonParseException, IOException {
		
		//scorre i JsonTokens
		while(jsonParser.nextToken() != JsonToken.END_OBJECT){
			String name = jsonParser.getCurrentName();
			if("id".equals(name)){
				jsonParser.nextToken();
				emp.setId(jsonParser.getIntValue());
			}else if("name".equals(name)){
				jsonParser.nextToken();
				emp.setName(jsonParser.getText());
			}else if("permanent".equals(name)){
				jsonParser.nextToken();
				emp.setPermanent(jsonParser.getBooleanValue());
			}else if("address".equals(name)){
				jsonParser.nextToken();
				//oggetto nidificato, chiamata ricorsiva
				parseJSON(jsonParser, emp, phoneNums, insidePropertiesObj);
			}else if("street".equals(name)){
				jsonParser.nextToken();
				emp.getAddress().setStreet(jsonParser.getText());
			}else if("city".equals(name)){
				jsonParser.nextToken();
				emp.getAddress().setCity(jsonParser.getText());
			}else if("zipcode".equals(name)){
				jsonParser.nextToken();
				emp.getAddress().setZipcode(jsonParser.getIntValue());
			}else if("phoneNumbers".equals(name)){
				jsonParser.nextToken();
				while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
					phoneNums.add(jsonParser.getLongValue());
				}
			}else if("role".equals(name)){
				jsonParser.nextToken();
				emp.setRole(jsonParser.getText());
			}else if("cities".equals(name)){
				jsonParser.nextToken();
				while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
					emp.getCities().add(jsonParser.getText());
				}
			}else if("properties".equals(name)){
				jsonParser.nextToken();
				while(jsonParser.nextToken() != JsonToken.END_OBJECT){
					String key = jsonParser.getCurrentName();
					jsonParser.nextToken();
					String value = jsonParser.getText();
					emp.getProperties().put(key, value);
				}
			}
		}
	}

}

JsonParser è l’API di streaming json di jackson per leggere dati json, lo stiamo utilizzando per leggere i dati dal file e quindi il metodo parseJSON() viene utilizzato per scorrere i token e elaborarli per creare il nostro oggetto java. Notare che il metodo parseJSON() viene chiamato ricorsivamente per “indirizzo” perché è un oggetto nidificato nei dati json. Per il parsing degli array, stiamo scorrendo il documento json. Possiamo utilizzare la classe JsonGenerator per generare dati json con API di streaming.

package com.journaldev.jackson.json;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Set;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.journaldev.jackson.model.Employee;

public class JacksonStreamingWriteExample {

	public static void main(String[] args) throws IOException {
		Employee emp = JacksonObjectMapperExample.createEmployee();

		JsonGenerator jsonGenerator = new JsonFactory()
				.createGenerator(new FileOutputStream("stream_emp.txt"));
		//per la stampa formattata
		jsonGenerator.setPrettyPrinter(new DefaultPrettyPrinter());
		
		jsonGenerator.writeStartObject(); // start root object
		jsonGenerator.writeNumberField("id", emp.getId());
		jsonGenerator.writeStringField("name", emp.getName());
		jsonGenerator.writeBooleanField("permanent", emp.isPermanent());
		
		jsonGenerator.writeObjectFieldStart("address"); //start address object
			jsonGenerator.writeStringField("street", emp.getAddress().getStreet());
			jsonGenerator.writeStringField("city", emp.getAddress().getCity());
			jsonGenerator.writeNumberField("zipcode", emp.getAddress().getZipcode());
		jsonGenerator.writeEndObject(); //end address object
		
		jsonGenerator.writeArrayFieldStart("phoneNumbers");
			for(long num : emp.getPhoneNumbers())
				jsonGenerator.writeNumber(num);
		jsonGenerator.writeEndArray();
		
		jsonGenerator.writeStringField("role", emp.getRole());
		
		jsonGenerator.writeArrayFieldStart("cities"); //start cities array
		for(String city : emp.getCities())
			jsonGenerator.writeString(city);
		jsonGenerator.writeEndArray(); //closing cities array
		
		jsonGenerator.writeObjectFieldStart("properties");
			Set keySet = emp.getProperties().keySet();
			for(String key : keySet){
				String value = emp.getProperties().get(key);
				jsonGenerator.writeStringField(key, value);
			}
		jsonGenerator.writeEndObject(); //closing properties
		jsonGenerator.writeEndObject(); //closing root object
		
		jsonGenerator.flush();
		jsonGenerator.close();
	}

}

JsonGenerator è facile da usare in confronto a JsonParser. Questo è tutto per il tutorial di riferimento rapido all’API Java Parser JSON di Jackson. L’API Java JSON di Jackson è facile da usare e offre molte opzioni per la comodità degli sviluppatori che lavorano con i dati JSON. Scarica il progetto dal link sottostante e gioca con esso per esplorare più opzioni riguardo l’API Json di Jackson.

Scarica il Progetto JSON di Jackson

Riferimento: Pagina GitHub di Jackson

Source:
https://www.digitalocean.com/community/tutorials/jackson-json-java-parser-api-example-tutorial