Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Java e JSON: un esempio di interazione

Scambiare i dati utilizzando il formato più usato in Ajax
Scambiare i dati utilizzando il formato più usato in Ajax
Link copiato negli appunti

Per sviluppare Rich Internet Application (RIA), occorre abilità nel realizzare l'interfaccia lato client, spesso basata su JavaScript e sulle diverse librerie come jQuery, MooTools, etc. È importantissimo però che il colloquio con la parte server, di cui ci occupiamo in questo articolo, sia il più semplice ed efficace possibile per ottenere applicazioni fluide e altamente interattive.

Il client interroga il server asincronamente per recuperare piccole quantità di dati e la prima forma di comunicazione è stata XML (da cui la X di AJAX). Ma il parsing di un documento XML può risultare un'operazione complessa a seconda della tipologia di dati restituiti dal server.

Per semplificare questo scambio i creatori di JSON hanno dato alla luce un formato meno strutturato rispetto a XML e più leggero da utilizzare.

JSON lato client

In questo articolo non ci soffermiamo troppo sull'uso di JSON lato client, per approfondire questa parte possiamo utilizzare un precedente articolo pubblicato su JavaScript.HTML.it. L'articolo mostra come questa notazione ci permetta di avere una serie di servizi di utilità di base per racchiudere, in una chiamata asincrona (tramite oggetto XMLHTTPRequest), una serie di informazioni e poterne disporre in oggetti Javascript.

Giusto per chiarire gli esempi che seguiranno, ecco una tipica risposta JSON:

{
  articolo : "AJAX e JSON",
  pubblicato : true,
  interessante : null,
  data  : [18, 5, 2006],
  info  : {autore: "Andrea Giammarchi"}
};

JSON lato server

Scopo del nostro articolo è manipolare richieste e risposte lato server, per supportare gli sviluppatori front end con i dati di cui questi hanno bisogno. Vedremo come costruire un servizio per una applicazione web in Java per trasformare oggetti più o meno complessi di Java (una classe Articolo, per esempio) automaticamente per mezzo di librerie che "serializzano" un oggetto Java in una stringa JSON.

Riprendiamo la stringa JSON vista nel precedente paragrafo. Questa catena di testo potrebbe essere rappresentata in Java dalla classe:

package it.html.json;
public class Articolo {
	private String articolo;
	private boolean pubblicato;
	private boolean interessante;
	private int [] data;
	Info info;
	// Metodi getter e setter
	...
}
class Info {
	private String autore;
	// Metodi getter e setter
	...
}

Per poter serializzare dovremmo creare un metodo di logica di business che occupi di recuperare tutti i metadati via Reflection e costruire la stringa in maniera ricorsiva (in quanto ogni classe può contenere altre classi, come in questo esempio).

Ovviamente sarebbe un lavoraccio, per fortuna possiamo affidare questa attività ad uno delle decine di progetti che si trovano in rete a tal proposito. Sul sito json.org troviamo librerie per tutti i linguaggi.

In questo articolo utilizziamo la libreria Jabsorb, già nota come JSON-RPC.

Esempio di JSON in Java

L'esempio che vedremo si compone di una servlet il cui scopo è di prendersi carico delle richieste asincrone provenienti dall'interfaccia e rispondere ad alcune logiche di business: nel nostro caso restituire una lista (JSON, ovviamente) di oggetti Articolo e un oggetto Articolo ricercato per identificativo (id).

Partiamo dicendo che grazie alla serializzazione/de-serializzazione del java bean non dovremo preoccuparci di creare logica nuova, ma riutilizzeremo i metodi di logica di business già presenti per l'applicazione. Nel nostro caso ipotizziamo di disporre della seguente interfaccia:

package it.html.json;
public interface ArticoloManager {
	public Articolo[] getAllArticles();
	public Articolo getArticolo(String id);
	public void insertArticolo(Articolo a);
  /* ... */
}

Lo scopo di questi metodi è evidentemente recuperare tutti gli articoli, o uno solo, dalla base di dati, più altri eventuali metodi che non ci interessano ora per la discussione. L'idea è di mostrare come sia possibile e facile implementare una soluzione JSON riutilizzando strati di software già esistenti.

La parte interessante è la creazione della servlet che sarà interrogata. Giusto per dare un minimo di semantica all'esempio, immaginiamo che la nostra RIA crei un widget con informazioni di dettaglio su un articolo quando l'utente vi passa sul titolo con il mouse:

  1. L'utente passa il mouse su un articolo
  2. via JavaScript si costruisce la richiesta (per esempio http://server/ajaxcontroller?op=listArticle&id=xyz)
  3. l'oggetto XMLHttpRequest invia la richiesta
  4. la nostra servlet riceve la richiesta e risponde con la catena di testo: {articolo : "JSON e Java", pubblicato : false, interessante : true, data : [09, 08, 2009], info : {autore: "Pasquale Congiustì"}}
  5. alla ricezione della stringa verrà valutata (eval()) via JavaScript e verrà creato un paragrafo al volo (modificando il DOM ad esempio) formattato con quelle informazioni
  6. L'utente visualizza le informazioni

Vediamo la prima parte della servlet:

package it.html.json.controller;
/**
 * @author Pasquale Congiusti
 *
 * Ajax service dispatcher
 */
public class AjaxController extends HttpServlet
{
  // L'interfaccia che ci permette di accedere alla logica di business
  ArticoloManager aManager;
  // l'oggetto che ci permetterà di serializzare/de-serializzare i parametri
  JSONSerializer serializer;
  public void init()
  {
    serializer = new JSONSerializer();
    // Usiamo una classe factory che genera un gestore concreto
    // della logica di business (immaginiamo di averne
    // a disposizione una chiamata ArticoloManagerConcrete)
    aManager = new ArticoloManagerConcrete();
    try
    {
      // inizializza i tipi serializzatori forniti
      // di default con la libreria
      serializer.registerDefaultSerializers();
    }
    catch (Exception e)
    {
      log.fatal ( "FATAL Exception " + e.getMessage () );
    }
  }
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
                         throws ServletException, IOException
  {
    // Il parametro "op" stabilisce l'azione da che effettua il dispatcher
    String op = request.getParameter("op");
    try
    {
      if (op != null)
      {
        if (op.equalsIgnoreCase("listAllArticles"))  listAllArticles(request, response);
        else if (op.equalsIgnoreCase("listArticle")) listArticle(request, response);
        // nessuna "op" trovata
        else log.warn("Asynchronous call op=" + op + " does not exists");
      }
      // op è null
      else log.warn("Asynchronous call null");
    }
    catch (Exception e)
    {
      log.error("General error (" + e.getClass() + "): " + e.getMessage());
    }
  }

Di interessante vediamo le due variabili di istanza che vengono inizializzate al momento della creazione dell'oggetto.

Uno è il manager di cui abbiamo discusso, l'altro è una classe facente parte della libreria che utilizzeremo per le operazioni di serializzazione e de serializzazione. Il metodo che segue l'init() è il metodo service() che usiamo come semplice dispatcher per soddisfare le diverse richieste (in questo caso due).

// Restituisce i dati di un articolo in formato JSON
  //Parametri attesi: op, operazione; id, indentificativo articolo
  private void listArticle(HttpServletRequest request, HttpServletResponse response)
                           throws NumberFormatException, IOException, MarshallException
  {
    String articoloId = request.getParameter("id");
    // Usa la logica esistente per recuperare l'articolo
    Articolo toRet = aManager.getArticolo(articoloId);
    // restituisce l'articolo serializzato in JSON tramite
    // l'oggetto serializer
    response.getWriter().println(serializer.toJSON(toRet));
  }
  // Restituisce una lista di articoli in formato JSON
  // Parametri attesi: op, operazione
  private void listAllArticles(HttpServletRequest request, HttpServletResponse response)
                               throws NumberFormatException, IOException, MarshallException
  {
    // Recupera gli articoli con la logica esistente
    Articolo[] toRet = aManager.getArticles();
    // returns a JSON
    response.getWriter().println(serializer.toJSON(toRet));
  }
  private String streamToString(InputStream in) throws IOException
  {
    StringBuffer out = new StringBuffer();
    byte[] b = new byte[4096];
    for (int n; (n = in.read(b)) != -1;) {
      out.append(new String(b, 0, n));
    }
    return out.toString();
  }

La struttura delle richieste è identica. All'inizio si recuperano i parametri necessari al metodo di logica, poi si chiama il metodo di logica e si passa il risultato al serializer, scrivendone sullo stream di output le informazioni recuperate.

Abbiamo anche definito streamToString un metodo privato di utilità, che ci consente di recuperare delle stringhe dall'InputStream in ingresso.

I due metodi sopra mostrano che praticamente non dobbiamo fare nulla di nuovo rispetto al concetto server side, cioè eseguire una funzione (effettuando i dovuti controlli che qui mancano) e restituire un risultato. La formattazione del risultato è affidata allo statement: serializer.toJSON(toRet).

Quello che abbiamo visto si applica quando ci troviamo nel caso più semplice e cioè quando facciamo delle richieste HTTP in stile GET (passando quindi i parametri nella URL). Ma spesso capita di dover passare al server molto di più di un semplice identificativo. Si pensi alla creazione di complesse maschere di ricerca o in generale di inserimento dati che devono essere passate al server asincronamente. In questo caso la cosa migliore è passare i dati in POST e inserire nel body proprio degli oggetti in notazione JSON.

Senza introdurre ulteriore complessità alla discussione, immaginiamo che di avere un form per inserire informazioni proprio su un articolo e che dinamicamente, utilizzando JavaScript, costruiamo la seguente stringa JSON che rappresenta un nuovo articolo da inserire nella base dati.

{
  articolo : "Buon Compleanno",
  pubblicato : false,
  interessante : true,
  data  : [18, 08, 1981],
  info  : {autore: "Pasquale Congiustì"}
};

Questa stringa viene inserita nel body della richiesta dal client Javascript prima di inviare la richiesta asincrona. Compito del nuovo metodo della servlet sarà quello di recuperare il contenuto JSON, deserializzarlo in oggetto Java e passarlo al manager.

/*
   * Restituisce un Articolo in formato JSON
   *
   * Parametri attesi: op, operazione
   */
  private void insertArticle(HttpServletRequest request, HttpServletResponse response)
                             throws NumberFormatException, IOException, MarshallException
  {
    // riceve il messaggio JSON da POST
    String json = streamToString(request.getInputStream());
    // lo de-serializza
    Articolo newArticle = (Articolo) serializer.fromJSON(json);
    // inserisce l'articolo ricevuto nel DB
    aManager.insert(newArticle);
  }

Il metodo insertArticle() in sé è molto semplice in quanto si occupa di recuperare la catena JSON attraverso un altro metodo, convertirla in oggetto Java attraverso la libreria Jasorb e infine passare il risultato al manager.


Ti consigliamo anche