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

AJAX e JSON, accoppiata vincente

Approfondire il formato JSON per l'ottimizzazione di transazioni asincrone AJAX
Approfondire il formato JSON per l'ottimizzazione di transazioni asincrone AJAX
Link copiato negli appunti

L'esigenza di potersi interfacciare in modo universale con diverse tecnologie ha dato luce a JSON (JavaScript Object Notation), un sistema di scambio dati molto semplice basato su una formattazione di testo tale da rendere differenti tipi di variabili facilmente rappresentabili e comprensibili da occhi umani come dai linguaggi di scripting o programmazione più diffusi.

JSON è basato sugli standards ECMA-262 terza edizione e sulla programmazione in Javascript, linguaggio basato su questo stesso standard.

Questo formato permette a tecnologie differenti di scambiare informazioni e si rivela anche un buon metodo per interagire con chiamate asincrone, poichè permette di inviare diversi tipi di dati sotto forma di stringhe ben interpretabili dalla quasi totalità dei linguaggi server.

JSON, una stringa per tutti

Quali dati si possono convertire

I tipi di dato che possiamo usare con JSON per lo scambio di informazioni sono due:

  • array con soli indici numerici
  • oggetti

La scelta limitata è ricaduta su questi tipi di contenitori perché presenti nella maggior parte dei linguaggi di sviluppo. Anche i tipi che si possono racchiudere al loro interno vedremo essere abbastanza comuni.

Essi infatti possono contenere dati primitivi come:

  • numeri
  • booleani
  • stringhe
  • valore nullo (null)

oppure altri array o altri oggetti. Poremmo avere infatti oggetti popolati da array, array di array ed oggetti di oggetti. Ecco un esempio di variabili che si possono inviare o ricevere attraverso JSON:

Listato 1. Dichiarazioni di dati compatibili JSON

// oggetto generico che contiene
// tutti i tipi di dato compatibili con JSON
var oggetto = {
  articolo : "AJAX e JSON",
  pubblicato : true,
  interessante : null,
  data  : [18, 5, 2006],
  info  : {autore: "Andrea Giammarchi"}
};

// array di valori misti
var arr = ["uno", 2, 3.01, (2 === (2*2/2)), null, oggetto];

// array di array
var arr2 = [arr, arr, arr, arr, arr];

Usare JSON per l'invio di solo testo, di un numero o di un songolo valore booleano non è invece un approccio utile né adatto alla conversione/riassegnazione dei valori, che dovranno essere sempre racchiusi all'interno degli unici contenitori riconosciuti, array ed oggetti, anche qualora non dovessero avere alcun dato.

Listato 2. Inviare una singola stringa

// variabile non adatta per JSON
var str = "test";

// variabili adatte per JSON
var arr = [str];
var obj = {testo:str};

Conversione dei dati in formato JSON

Esaminiamo le regole principali per la conversione dei dati e vediamo come rappresentare attraverso stringhe i vari tipi.

  • Il valore null equivale alla stringa null.
  • I numeri vengono convertiti nella loro rappresentazione testuale ad esempio 100 sarà la stringa 100
  • come i valori booleani, che saranno equivalenti alle stringhe true o false.

Un'attenzione particolare va rivolta alle stringhe, le quali sebbene possano apparentemente sembrare il tipo di dato più semplice da convertire, sono le più complesse a causa del set di caratteri "unicode", supportato da JavaScript, ma non sempre presente o supportato allo stesso modo da altri linguaggi.

La regola principale è quella di racchiudere le stringhe tra doppi apici, "...", mentre quella fondamentale di convertire il contenuto di tale stringa in modo comprensibile per tutti, a prescindere dal tipo di codifica supportata.

Oltre a questo, che verrà spiegato e mostrato più dettagliatamente poi, è necessario aggiungere qualche backslash '' prima di alcuni caratteri speciali quali il tabulatore (t), ritorno a capo (n), doppi apici stessi (") ed altri ancora, al fine di preservarli.

Tutte queste regole possono essere applicate ad uno o ad un insieme di elementi, rappresentabili all'interno di un array. Un array è racchiuso tra parentesi quadre e gli elementi sono separati da una virgola ','.

Esempio di array JSON

"[elemento0,elemento1,elemento2,elementoN]"

Tra gli elementi e le virgole non sono ammessi spazi. I parser li interpreterebbero male e potrebbero verificarsi errori di conversione. Dobbiamo evitare quindi rappresentazioni tipo "[elemento1, elemento2]".

Stesso discorso vale per gli oggetti, i quali però sono composti da coppie "nome:valore" associate col carattere ':' (due punti). Le coppie sono separate tra loro da virgole e tutto l'oggetto è compreso in parentesi graffe.

Esempio di oggetto JSON

"{"parametro1":"valore1","parametro2":"valore2","parametroN":"valoreN"}"

Vediamo un esempio concreto di conversione.

Listato 3. JSON, conversione dei dati da JavaScript

<script type="text/javascript">

// variabili semplici
var nulla = null;
var booleano = true;
var numero = 1.234;
var stringa = "ciao mondo!";

// variabili utili per uno scambio dati in JSON

// array contenente l'insieme delle variabili diverse di cui sopra
var arrayElementi = [nulla, booleano, numero, stringa];

// oggetto con un insieme di parametri contenenti variabili diverse
var oggetto = {
  nulla   : nulla,
  booleano  : booleano,
  numero  : numero,
  stringa  : stringa,
  arrayElementi : arrayElementi
};

// esempio dell' array in formato JSON
// per questo esempio sfruttiamo una semplice funzione
// adatta solo al tipo di array creato in precedenza
function arrayElementiPerJSON() {

  // stringa da restituire
  var risultato = "[";

  // ciclo l'array
  for(var a = 0, b = arrayElementi.length; a < b; a++) {

    // verifico il tipo di dato
    switch(typeof(arrayElementi[a])) {

      // per dati primitivi di tipo booleano o numero
      // o per oggetti di questo tipo basta sfruttare il toString()
      case ("boolean" || arrayElementi[a] instanceof Boolean):
      case ("number" || arrayElementi[a] instanceof Number):
        risultato += arrayElementi[a].toString() + ',';
      break;

      // mentre per una stringa bisogna aggiungere gli apici
      case ("string" || arrayElementi[a] instanceof String):
        risultato += '"' + arrayElementi[a] + '",';
      break;

      // nel caso di null, unico non ancora considerato tra
      // i possibili elementi di questo array, si aggiunge semplicemente null
      default:
        risultato += 'null,';
      break;
    };
  };

  // non resta che restituire la variabile risultato
  // dopo aver tolto l'ultimo carattere virgola ed aver aggiunto
  // la parentesi quadra per chiudere l'array
  return risultato.substr(0, risultato.length - 1) + ']';
};

document.write(arrayElementiPerJSON() + '<br />');
// [null,true,1.234,"ciao mondo!"]

// una procedura dedicata o analoga per l'oggetto creato in precedenza
// darebbe questo risultato
// {"nulla":null,"booleano":true,"numero":1.234,"stringa":"ciao mondo!","arrayElementi":[null,true,1.234,"ciao mondo!"]}
document.write('{"nulla":null,"booleano":true,"numero":1.234,"stringa":"ciao mondo!","arrayElementi":[null,true,1.234,"ciao mondo!"]}' + '<br />');
</script>

L'esempio appena visto serve a mostrare i concetti alla base di una conversione ma non è affatto indicato per applicazioni reali poichè il concatenamento dei tipi di dato deve essere sia ricorsivo, utile per variabili innestate di array o oggetti, sia più dettagliato, soprattutto per quello che riguarda la conversione delle stringhe, dove i vari caratteri unicode, che potrebbero andare persi o non essere interpretati correttamente dalle varie tecnologie, richiedono una traduzione apposita.

Ad esempio i caratteri contrassegnati dal prefisso u, che servono a descrivere il corrispettivo carattere in esadecimale, non hanno spesso equivalenti negli altri linguaggi. Da JavaScript a PHP, inviare una stringa di questo tipo "testu0040html.it" non porterà alla ricezione della stringa test@html.it, ma della stringa originale.

Per risolvere problemi di caratteri e conversione dati c'è una comoda funzione JavaScript più o meno ufficializzata dal sito di riferimento di JSON e creata appositamente per convertire con una sola chiamata qualunque tipo di array o oggetto e di recuperarne a sua volta con altrettanto facilità il valore originale.

Il file in esame può essere esaminato o scaricato direttamente da
questa pagina, oppure in versione compressa ma ben descritta in
questa. Sempre nel sito ufficiale di JSON sarà possibile trovare maggiori dettagli sui tipi di valori e la relativa conversione.

Conversione a dato nativo da una stringa JSON

Ricreare una variabile da una stringa formattata correttamente è l'operazione più semplice e veloce che ci possa essere, a prescindere dalla complessità o lunghezza del dato in ingresso e sempre rimanendo in ambito JavaScript. Questo grazie alla funzione eval(), in grado di valutare quasi in tempo reale una stringa come se fosse codice scritto direttamente dallo sviluppatore.

Un array mediamente complesso, da migliaia di elementi innestati di vario tipo, può essere quindi tranquillamente riassegnato in una manciata di decimi di secondo. Questo esempio mostra quanto appena detto, dove è palese notare come la differenza tra l'assegnazione manuale e quella in JSON sia praticamente inesistente.

Quello che invece rallenta in modo consistente lo scambio dati è proprio la conversione in stringa a partire dal dati originali.

Sebbene la situazione mostrata nell'esempio sia abbastanza complessa e sicuramente più idonea ad una intranet che ad uno scambio dati on-line, vista la mole di caratteri da inviare o ricevere, è sempre corretto tenere a mente questo collo di bottiglia in conversione.

Ad esempio l'invio in JSON dell'intero contenuto di una tabella di un database potrebbe essere fattibile, sarebbe consigliabile però spezzettare le informazioni inviando 100 record per volta o anche meno a seconda dei contenuti, per non creare rallentamenti all'utente ed al server, visti dei calcoli richiesti per queste conversioni.

Il prototype di stringa dedicata, assegnato nel file json.js, che ha il compito di verificare il corretto formato e comportamento della stringa in fase di riassegnazione, potrebbe rallentare ulteriormente la trasmissione. È apparentemente inutile ma tecnicamente importante sfruttare questo metodo al posto del solo eval, infatti eval valuta ogni tipo di codice col rischio di elaborare anche del codice "malign0" o non desiderati.

Listato 4. Esempio di struttura potenzialmente pericolosa

// stringa formattata in modo non corretto

var stringaJSON = '["prova","codice maligno",alert("salve funzione eval")]';

// Metodo Insicuro:
// assegnazione a tipo dato nativo

var arrayDaJSON = eval('(' + stringaJSON + ')');
// si apre la finestra di avviso con scritto
// salve funzione eval

// Metodo Corretto:
//  var arrayDaJSON = stringaJSON.parseJSON();
//  arrayDaJSON sarà assegnata come false, nessun errore

Visto che lo scopo di questo articolo è quello di sfruttare JSON attraverso AJAX ed essendo quest'ultimo capace di essere integrato in numerose tipologie di applicativi, non bisogna mai dimenticarsi di questo semplice ma fondamentale accorgimento.

Conversione da e verso un linguaggio server side

Come già accennato, parte del successo di JSON è dovuto dal porting più o meno ufficiale di numerose librerie, moduli o classi, create per altrettanti numerosi linguaggi server side.

C, come C++ o C#, Java, Perl, PHP, Python, Ruby e chi più ne ha più ne metta, hanno tutti una o più soluzioni per interagire nel miglior modo possibile con stringhe formattate in JSON e di generare a loro volta stringhe compatibili.

Anche lato client, oltre al JavaScript, c'è almeno un'altro linguaggio in grado di sfruttare questo tipo di interscambio dati. È il caso di ActionScript, linguaggio di scripting usato da Flash, il quale permette di riutilizzare le stesse tecniche, predisposte con JSON lato server, su client diversi.

Mentre ActionScript è basato a sua volta sullo stesso standard del JavaScript e riesce quindi in modo quasi naturale ad interagire con stringhe formattate, molti degli altri linguaggi non sono in grado di interpretare o convertire nativamente le informazioni contenute in questo formato.

Nel caso di PHP, che non supporta nativamente unicode, non è detto che la stringa sia interpretata in modo sempre corretto come non è detto che le operazioni interne al parser non siano compromettenti per i tempi di risposta dell'applicativo.

La classe PEAR ad esempio è ben fatta ed ottimizzata al dettaglio, ma per diverse ragioni risulta troppo lenta, se non accelerata con appositi sistemi, soprattutto a causa delle verifiche incrociate che deve effettuare per ogni carattere di ogni stringa presente all'interno di ogni oggetto o array.

Il consiglio quindi, è quello di utilizzare la versione compilata, la quale effettua la conversione in modo estremamente più performante, aumentando le possibilità di inerazione senza appesantire troppo il server.

Se usiamo C#, Java o altri pseudo compilabili, non ci sono contro indicazioni di rilievo, sia perchè alcuni hanno il supporto per unicode, sia perchè sono decisamente più veloci.

Una lista completa di tutti i vari porting è presente ancora una volta nel sito ufficiale di JSON.

Nella parte precedente dell'articolo abbiamo esaminato la codifica JSON ed i problemi legati alla conversione. Questa parte invece è dedicata all'uso di JSON nello scambio asincrono di informazioni.

Dati complessi? non necessariamente

Non è detto che sia sempre necessario disturbare il motore JSON. La caratteristica del formato permette di riconoscere facilmente un valore JSON da tutti gli altri. È sufficiente un controllo sul primo carattere: se diverso da '[' o da '{' farà sicuramente parte di una stringa che non richiede una conversione dal formato JSON.

Un approccio di questo tipo però implica una serie di controlli aggiuntivi, ad esempio una stringa non JSON, come '[1,2,3]' inserita in una chat, si presenta in formato JSON ma non è corretto interpretarla come tale.

Potremmo ovviare rappresentando tutti i dati come stringhe JSON, inviandoli come oggetti semplici, tipo '{valore:variabile}', così da non avere alcun problema in conversione e mantenendo comunque l'universalità del dato a prescindere dalla sua natura.

Strutturando l'applicativo sempre su conversioni JSON, sarà quindi possibile trasportare il front-end dello stesso su numerosissime piattaforme, senza dover effettuare alcuna modifica lato client.

Un primo assaggio

Informazioni tipiche di applicativi web, ad esempio quelle di un prodotto, di un cliente o di un utente di un forum, possono essere rappresentate come oggetti, comodissimi da gestire sia lato client che lato server, grazie anche ad un eventuale XML o database con campi di tabelle chiamati allo stesso modo.

Un esempio non troppo banale che mostra JSON all'opera su quella che potrebbe essere una pagina fatta per interazioni asincrone è realizzato in questa pagina.

Figura 1. Risultato dell'esempio
Risultato dell'esempio

Inserendo un bottone di tipo submit, AJAX invia la stringa mostrata in fondo alla pagina al server, per avere su quest'ultimo un dato ben definito e sicuramente più semplice o naturale da gestire.

Un eventuale implementazione degradabile sarebbe comunque possibile, sfruttando JSON anche da parte del linguaggio usato dal server.

Quindi non solo si possono gestire dati più evoluti in interazione asincrona ma si può anche basare interamente una gestione back-end su stringhe di tipo JSON al fine di sfruttare gli stessi controlli e le stesse operazioni sia che il client supporti JavaScript ed AJAX sia che non li supporti.

... e XML?

Lo scopo principale di XML è molto simile a quello di JSON ovvero permettere a numerosi linguaggi di scambiare ed elaborare informazioni.

Non è possibile mettere in discussione l'universalità o il successo dell'XML, tanto meno le sue potenzialità. Sono rappresentabili gerarchie semplici o estremamente complesse con la possibilità di ricostruire oggetti e variabili dalla lettura di attributi presenti nei nodi.

Esiste però un neo connaturato a XML: il numero elevato di caratteri necessari a rappresentare le informazioni. Se è vero che per un server leggere un file da 10Kb o da 1Mb è pressapoco equivalente, non lo è altrettanto per un client.

Questa caratteristica è tanto più evidente quanto più piccola è l'informazione da trasportare. Si arriva al paradosso di avere più caratteri per la descrizione del documenti, che per i dati veri e propri. Un utilizzo massiccio di attributi potrebbe aiutare a rosicchiare qualche carattere ma oltre a perdere in linearità e leggibilità non si riuscirebbe comunque a guadagnare troppo spazio.

È proprio in queste occasioni che JSON fa capolino, permettendo di rappresentare anche dati complessi senza aggiungere nulla all'infuori di qualche parentesi o qualche segno di punteggiatura. Il rapporto "peso del contenuto/informazioni presenti" va quindi a migliorare usando JSON invece di XML.

Bisogna analizzare anche le operazioni da svolgere una volta letta l'una o l'altra rappresentazione.

Quando AJAX effettua una richiesta asincrona non si limita a restituire il solo testo ma verifica che tale pagina sia di tipo XML e senza errori per poi riassegnare il tutto alla variabile responseXML, restituita come oggetto o documento nativo di tipo XML. Quello che deve fare JavaScript a questo punto è estrapolare con metodi o funzioni più o meno complesse i dati, solitamente al fine di rappresentarli nuovamente come oggetti, liste di informazioni o variabili comunque analoghe.

Con JSON tutto questo non avviene, sia perchè una pagina adibita ad uno scambio dati di questo tipo non mostrerà le intestazioni corrette per un foglio XML, evitando quindi al motore interno al client di riconvertire il contenuto in documento, sia perchè il parsing delle informazioni avverrà per intero con una sola chiamata alla prototype di stringhe parseJSON.

Inutile esporre quanto tempo si possa risparmiare per sviluppare interazioni avanzate come altrettanto superfluo dire che gli stessi client potranno godere di calcoli molto meno pesanti e di un utilizzo di banda ridotta.

Quando scegliere l'uno o l'altro sistema non è un argomento semplice da affrontare, ma almeno in una interazione tipica basata su liste ordinate di dati sarebbe difficile poter affermare che XML sia la soluzione più idonea.

Esempio pratico

Tra i primi applicativi AJAX di successo ce n'è uno che è stato sempre ai vertici della classifica, diventato quasi onnipresente e sfruttato dai big della rete, come dai siti più semplici: suggest, il suggerimento dinamico per la compilazione dei form.

I concetti che stanno alla base di un suggest sono semplici, aiutare o velocizzare la compilazione del campo di ricerca mostrando risultati possibili compatibil con il testo inserito parzialmente o per intero dall'utente. Questi risultati altro non sono che una lista di stringhe, restituite come liste (x)html, xml e nell'esempio proposto come stringa JSON.

Quale miglior metodo se non restituire un array composto da 10, 100 o migliaia di elementi? In realtà in questo esempio sono poco più di mille, e sono alcuni nomi propri di persona usati in Italia.

Scrivendo l'iniziale di un nome, il suggest mostrerà tutti quelli che cominciano per la stessa lettera e continuando a scrivere il resto del nome il suggest in automatico filtrerà quelli che non contengono i caratteri scelti.

Scrivendo quindi la lettera 'A' (oppure 'a'), ci saranno tutti, aggiungendo 'n', ci saranno tutti quelli che cominciano con 'an', e così fino al nome completo.

Una piccola analisi

In questo esempio, dove l'archivio è fatto di soli nomi, si presume che la ricerca cominci con l'iniziale ed è quindi più facile aiutare l'utente. Solitamente però il suggest lavora su tutte le stringhe e non solo su quelle che "cominciano per".

Questo significa che scrivendo 'a', un suggest potebbe suggerire Andrea come Giorgia, magari ordinando i risultati in ordine alfabetico, e molto spesso questo il tipo di aiuto che un utente si aspetta all'interno di un sito ricco di contenuti.Effettivamente è quello il modo corretto mentre questo proposto è solo un semplice esempio che non prevede un database.

Un server può trovarsi sovraccarico di richieste o stressato da numerose interazioni simultanee. Se non si sfruttano le richieste già fatte attraverso variabili o cookie sul client, esse si moltiplicano inutilmente.

Ad esempio scrivendo 'a', oltre a ricevere i suggerimenti, il risultato dovrebbe essere messo in qualche modo in cache, perchè non avrebbe senso richiedere questa sola lettera al server una seconda volta.

Una libreria molto nota e sicuramente ben fatta, che però non implementa alcun sistema di caching delle richieste, script.aculo.us, la quale permette di aggiungere su una pagina web un suggest completo grazie a pochissime linee di codice.

Questa libreria però ha inserito un controllo sulla latenza trascorsa tra l'attimo in cui l'utente smette di scrivere e l'avvio della richiesta sul server, evitando così di effettuare tante richieste quanti sono i caratteri digitati.

L'insieme di questi accorgimenti potrebbe evitare dover lavorare con pochi dati per volta, poichè tale lista, grazie a JSON, potrebbe contenerne centinaia, implicando un'attesa lievemente maggiore per il client, ma garantendo una rapidità o efficacia di suggerimento senza pari per tutte le richieste successive.

Per capire meglio quanto detto ecco finalmente il link all'esempio, dove i commenti presenti nei vari files javascript, in particolare utenti.js, dovrebbero spiegare bene come provare a creare un sistema suggest ancora migliore o appoggiato ad un vero database.

Esempio suggest nomi persone.

Considerazioni finali

JSON ha veramente tanti vantaggi e pochi limiti realmente insormontabili. Compete con XML su interazioni semplici o mediamente complesse e permette di risparmiare banda o di inviare molti più dati con la stessa che avrebbe richiesto un file di quest ultimo tipo.

Va segnalato che l'applicativo proposto in questo articolo, basato su files XML sarebbe pesato circa 42Kb contro i 16Kb dell'attuale.

Portabilità, potenza e velocità ai massimi livelli sul client, serve molto altro per una rich internet experience? :-)

Ti consigliamo anche