La X di JSON – seconda parte

jlogo.jpgQuesta è la seconda parte di una serie di tre post dedicati a JSON, un formato javascript per la descrizione e lo scambio di dati. Nella prima parte abbiamo visto il raffronto tra la sua sintassi e quella dell’XML, campione in carica nella stessa categoria; oggi invece si parlerà di alcune differenze specifiche tra i due formati soffermandoci in particolare sul loro ruolo nell’ interazione client-server.(con degli esempi da scaricare)

JSON sfida XML

JSON povrebbe avere le carte in regola per scalzare XML? Per avere qualche risposta ho dato uno sguardo alla ‘comparazione ‘ufficiale’ sul sito JSON, ad un raffronto indiretto di Peter-Paul Koch, alla pagina ‘messaggi JSON’ su AjaxPatterns e ad alcuni miei appunti presi su uno scontrino mentre rivedevo il post precedente.
Prendiamo come punto di partenza la frase:

It (JSON) is easy for humans to read and write.

Ne siamo davvero sicuri? E’ vero sia le ‘Persone’ – ovvero gli sviluppatori che creeranno, leggeranno e modificheranno i dati – che per i ‘Software’, ovvero le applicazioni lato server e client che i dati li dovranno generare o processare?
Creazione del codicePersone: Sono opposti due diversi modi di scrivere del codice: un linguaggio di markup (come XML) si scontra con un linguaggio di scripting (come Javascript).
Aprire e chiudere dei tag seguendo la giusta gerarchia è probabilmente più immediato che rispettare l’apertura e la chiusura di numerose parentesi graffe, quadre e tonde.
Tuttavia, un codice JSON risulta più snello sotto due aspetti:

~ La ridondanza nella descrizione dei nomi, ovvero il contenimento di dati tra tag aperti e chiusi:

    <colore>giallo</colore>

è sostituita da delle semplici coppie nome valore:

   "colore": "giallo"

~ Due o più elementi con lo stesso nome:

    <colore>magenta</colore>
    <colore>porpora</colore>
    <colore>verdognolo</colore>

possono essere ricompattati in un array:

    "colori": ["magenta","porpora","verdognolo"]

Estese queste regole ai vari oggetti e coppie nome/valore al loro interno, la sintassi JSON risulterà senza dubbio più leggera di quella XML. Secondo il mio punto di vista, però:

~ Oltre il terzo livello di innestamento – l’elemento radice possiede al suo interno almeno un elemento ‘nipote’ – ricomporre la gerarchia degli oggetti diventa molto complicato. (Vedi ad esempio il primo raffronto in questa pagina).

~ Come già detto la volta scorsa, JSON non ha uno DTD o uno schema che regoli la struttura del documento come accade con l’XML. Non esistono quindi delle regole su come gestire ad esempio gli attributi e gli elementi ‘mixed content’, ovvero quelli che hanno contemporaneamente come figli sia del testo che altri elementi.
La gestione degli attributi avviene rappresentandoli come il primo figlio di un elemento, annullandone quindi il ruolo di metadato associato a quel particolare elemento.
Riguardo ai ‘mixed content’, qualche giorno fa D. Crockford ha suggerito ad un tipico lamer logorroico (ma di sicuro dotato di un discreto fascino) di creare un figlio di nome “content” ed associargli il valore del testo inserito all’interno dell’elemento. Un’altro esempio è dato dalla ‘”serializzazione JSON del risultato delle query” in SPARQL, un linguaggio di interrogazione di RDF (a sua volta uno standard per la descrizione di metadati sotto forma di grafi) in cui il contenuto di un elemento è identificato dal nome “value”.

Software: Un qualsiasi linguaggio lato server non servirà che ad automatizzare – semplificandolo – il lavoro dello sviluppatore, per cui le differenze sono le stesse di sopra.

Lettura del codicePersone: Un codice dev’essere leggibile agli ‘umani’ soprattutto per venire incontro allo sviluppatore stesso che dovrà provvedere ad un eventuale debugging. Riguardo l’XML è sicuramente facile capire a prima vista la gerarchia di un documento, e in questo sono di aiuto Mozilla ed Explorer che forniscono un’indentazione automatica nel rendering dei file. (potete provare con qualsiasi feed RSS)
Con JSON, invece, il debugging si fa davvero duro: a volte il javascript può essere generato in modo comprensibile, come ad esempio nei traduzione feed RSS trasformati in JSON da John Resig , alle volte invece generato in modo da comparire in una sola riga come ad esempio nelle API Yahoo.

Software: Avete mai sentito parlare di ‘Rich Internet Applications’? Fino a poco fa ero sicuro avesse a che fare con la new-economy durante la bolla speculativa, oppure quel tizio che è riuscito a vendere dei pixel a peso d’oro. Mannaggia, nulla di tutto questo: si tratta di un modo di concepire applicazioni web spostando una parte del carico di lavoro del server verso il lato client allo scopo di velocizzare le prestazioni globali. Uno di questi compiti potrebbe essere quello di analizzare e riutilizzare le informazioni provenienti dal server, direttamente tramite il browser, utilizzando alcuni linguaggi lato client come JavaScript.
Con l’XML questo può essere fatto in due modi:
1) Si compie il parsing dell’XML, ovvero si prepara una struttura dati modificabile attraverso il Document Object Model (DOM). Ottenuti i vari valori sarà possibile quindi creare dinamicamente degli elementi HTML.
2) Si utilizza l’ XSLT, un linguaggio di markup (è XML infatti) che trasforma un documento XML in qualsiasi altro formato (come HTML, SVG, PDF) selezionandone i nodi attraverso un linguaggio chiamato XPath. Il parsing e il controllo delle trasformazioni viene fatto in Mozilla dall’ XSLT Processor, mentre in Explorer da un apposito ActiveX (MSXML2-3); due librerie js che consentono di richiamare facilmente entrambi sono Sarissa ed il più recente Freja (è disponibile anche progetto targato Google dal nome AJAXSLT che addirittura emula il funzionamento dei due processori in javascript, ma è stato messo in stand-by dallo stesso autore che consiglia nel frattempo i due framework menzionati prima).

Utilizzando JSON, invece, tutto diventa meno complicato. Il parsing è fatto da un unica funzione di nome eval(): questa consente di eseguire qualsiasi stringa passata come parametro allo stesso modo di un normale codice javascript. In questo caso trasformerà un messaggio JSON in un oggetto Javascript facilmente utilizzabile dal DOM.
La funzioneeval(), tuttavia, non gode di una buona fama a causa della sua potenziale pericolosità: non c’è nessuna garanzia che la stringa non contenga delle funzioni che, eseguite nel nostro browser, possano provocare dei danni. Per questo motivo Crockford ha rilasciato una libreria che consente di fare il parsing dei JSON senza l’utilizzo di eval() e di convertire, viceversa, un oggetto Javascript in un testo JSON.

Se posso trarre una prima breve conclusione dai paragrafi sopra:
1) JSON non raggiungerà mai XML nel contenimento e nella descrizione dei dati.
L’estensibilità (la “X” di JSON) esiste fino ad un certo punto, e vedo molto difficile l’affidarsi ad un formato così complicato per la gestione di enormi quantità di dati. Il primo esempio che mi viene in mente è un ‘dizionario di italiano’ costituito da migliaia di elementi, ad ognuno dei quali sono associati dei metadati che ne completano il significato e collegamenti a molti altri elementi. Una struttura del genere potrebbe essere ipoteticamente descritta da un grafo RDF, che ha proprio come sintassi ufficiale l’XML.
2) JSON sembra invece una buona alternativa ad XML nel trasporto dei dati.
O meglio, nel trasporto di piccoli dati che potrebbero ad esempio provenire da risposte a delle query.
Vediamo subito qualche esempio ‘asincrono’ a proposito.

Torna su

AJAX o AJAJ?

Nella sezione precedente si accennava un “interazione veloce tra client e server”: il maggiore artefice del risultato, come un pò tutti sanno, prende il nome di XMLHttpRequest ed il suo utilizzo ha spiccato il volo un anno fa grazie ad una azzeccata operazione di branding tecnologico. L’ oggetto javascript in questione consentire di inviare richieste HTTP ad un server senza attendere una risposta relativa ad un invio precedente, come invece avviene nel normale flusso di comunicazione.
Approssimativamente lo scambio di informazioni avviene in questo modo:

  1. creazione dell’oggetto XMLHttpRequest
  2. apertura di una connessione tramite una funzione open con parametri il metodo HTTP (get,post, put…), l’URL della risorsa richiesta e una variabile booleana relativa all’asincronismo
  3. chiamata di una funzione al ricevimento della risposta da parte del server
  4. invio del corpo della richiesta

Il tutto può essere tradotto in codice (metodo GET) in questo modo:

var xmlhttp = (window.XMLHttpRequest)? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET", "http://musicsite.buz/song.php?id=332", true);
xmlhttp.onreadystatechange = handle;
xmlhttp.send(null);

function handle() {
   if (xmlhttp.readyState == 4) {
    if (http.responseText.indexOf('invalid') == -1) {
       var resp  = xmlhttp.responseXML;
       //e una serie di operazioni....  
   }
}

Tralascio volontariamente alcune particolarità (tempi di attesa, gestione dell’header, assegnazione cross-browser specifica, altri metodi HTTP etc.) per far luce su quello che ci interessa maggiormente, ovvero la risposta che arriverà dal server. Questa può essere identificata da due parametri diversi: responseXML, che contiene l’albero DOM ottenuto dal parsing del file XML originario compiuto dall’XMLHttpRequest; responseText, che rappresenta invece un parametro associato ad una risposta di tipo stringa e può essere utilizzato – come in questo caso – per testare un eventuale presenza di errori in ricezione.

Supponiamo che il server, tramite questo semplice script PHP, ci metta a disposizione una serie di dati (un paio di informazioni su una canzone) in due formati: XML e JSON .
Vogliamo quindi stampare le informazioni nella nostra pagina HTML inserendole in una lista di questo tipo:

  <dl class="song">
    <dt>titolo canzone</dt>
    <dd>Performed by: nome gruppo </dd>
    <dd>Published in: anno</dd>
  </dl>

Proviamo a farlo utilizzando i tre metodi visti nella sezione precedente: XML+DOM, XML+XSLT, JSON+DOM.
Con il primo e con il terzo metodo tramite il DOM verranno creati nuovi dei nodi HTML in cui saranno inserite le informazioni prese dalla risorsa. Con il secondo metodo, invece, gli stessi elementi HTML saranno creati direttamente con l’XSLT.

XML+DOM – Chiediamo al server i dati in formato XML:

<song>
   <title>Birdman</title>
   <group>McDonald and Giles</group>
   <year>1971</year>
 </song>

La funzione handle, con cui manipoleremo l’oggetto DOM ottenuto grazie all’XMLHttpRequest, sarà la seguente:

function handle() {
   if (xmlhttp.readyState == 4) {
    if (xmlhttp.responseText.indexOf('invalid') == -1) {
      var resp = xmlhttp.responseXML;
      var dlist = document.createElement('dl');
      dlist.className = 'song';
      var title = document.createElement('dt');                  
  title.appendChild(document.createTextNode(resp.getElementsByTagName('title')[0].firstChild.nodeValue));
     dlist.appendChild(title);
     var group = document.createElement('dd');
     group.appendChild(document.createTextNode('Performed by: ' + resp.getElementsByTagName('group')[0].firstChild.nodeValue));
     dlist.appendChild(group);
     var year = document.createElement('dd');
     year.appendChild(document.createTextNode('Published in: ' + resp.getElementsByTagName('year')[0].firstChild.nodeValue));
     dlist.appendChild(year);
     document.getElementById('content').appendChild(dlist);
	}
}


Accedere ad uno dei valori (ad esempio, title) non è così diretto: si seleziona la radice dell'albero, ovvero responseXML; si cerca al suo interno il primo tag di nome 'title'; si seleziona il primo nodo figlio e si prende il suo valore. La verbosità aumenterebbe se nel documento ci fosse, ad esempio, un tag di nome 'album' che contiene al suo interno un'altro tag di nome 'year'. Per accedere ad valore all'interno del nostro 'year' appartenente a 'song' bisognerebbe specificare meglio il percorso con un'ulteriore precisazione:

xmlhttp.responseXML.getElementsByTagName('song')[0].getElementsByTagName('year')[0].firstChild.nodeValue. 

Argh!

XML+XSLT - Avremo bisogno inizialmente di un foglio di stile XSLT (che ho chiamato stylesheet.xsl) che trasformi l'XML in HTML.
Il file contiene al suo interno questo codice:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
 <dl class="song">
    <dt><xsl:value-of select="//title"/></dt>
    <dd>Performed by: <xsl:value-of select="//group"/></dd>
    <dd>Published in: <xsl:value-of select="//year"/></dd>
  </dl>  
</xsl:template>
</xsl:stylesheet>


In questo caso la selezione dei nodi tramite XPath è stata immediata.
Se vogliamo fare una trasformazione lato client utilizzando il framework Freja, la funzione handle non è più necessaria. Freja si preoccuperà di inizializzare l'XMLHttpRequest e richiamare il processore XSTL: basterà indicargli solo la sorgente XML, l'XSLT e l'elemento nella pagina HTML in cui vogliamo 'appendere' il risultato.

var source  = new Model('../resource.php?type=xml');
var stylesheet = new View('xslt/stylesheet.xsl');
Controller.onLoadComplete = create;
Controller.loadAssets();

function create() {
  Controller.render(source, stylesheet, { placeholder: 'content' });
}

Le prestazioni dei fogli di stile XSLT crescono all'aumentare dei livelli gerarchici da gestire, per cui costituiscono la soluzione ideale (assieme ad XQuery, ma meglio non divagare) per la gestione dei dati XML. Per i gruppi di dati più piccoli come nel nostro caso, però, rimane forse consigliata la creazione dei nodi HTML tramite la funzione del metodo precedente. Come si dice in questi casi: il gioco non vale la cHandle.

(ehm....andiamo avanti)
JSON+DOM - Chiediamo ora al server i dati in formato JSON:

{ "song": { 
  "title" : "Birdman",
  "group" : "McDonald and Giles",
  "year" : 1971 
  } 

La creazione dell'XMLHttpRequest e la funzione handle saranno di nuovo necessari.

function handle() {
   if (xmlhttp.readyState == 4) {
    if (xmlhttp.responseText.indexOf('invalid') == -1) {
     var resp = eval('(' + xmlhttp.responseText + ')');
     var dlist = document.createElement('dl');
     dlist.className = 'song';
     var title = document.createElement('dt');                  
     title.appendChild(document.createTextNode(resp.song.title));
     dlist.appendChild(title);
     var group = document.createElement('dd');
     group.appendChild(document.createTextNode('Performed by: ' + resp.song.group));
     dlist.appendChild(group);
     var year = document.createElement('dd');
     year.appendChild(document.createTextNode('Published in: ' + resp.song.year));
     dlist.appendChild(year);
     document.getElementById('content').appendChild(dlist);
    }
}

Come detto prima, la funzione eval() fara il parsing della stringa JSON restituendo un oggetto javascript. Accedere agli elementi all'interno dell'oggetto sarà molto più semplice che analizzare l'albero DOM ricavato dall'XML. Se rifacciamo l'esempio di un ipotetico tag 'year' presente in un'altra sezione del codice sorgente, il valore dell' year' all'interno di song sarebbe ottenibile in questo modo:

eval('(' + xmlhttp.responseText + ')').song.year

ovvero come nell'handle.

Altre alternative - 1) Si può utilizzare per entrambi i formati, al posto del DOM W3C, il non-standard ma molto efficace innerHTML : xmlinnerhtml.js & jsoninnerhtml.js.
2) Si possono richiedere al server i dati formattati direttamente in HTML, avendo quindi la possibilità di inserendoli in un lampo nella nostra pagina. In questo caso, però, si va un pò fuori tema.

I tre esempi sono naturalmente disponibili per il download.

Una conclusione lessicale: sappiamo tutti che AJAX è l'acronimo di "Asynchronous Javascript and XML". Sostituendo l'XML con il JSON l'acronimo diventerebbe AJAJ, "Asynchronous Javascript and Javascript" - ma nel caso che abbiamo visto resterebbe un gioco un'altro XML, quello dell'XMLHttpRequest.
Come abbiamo visto, eval può fare il parsing di JSON ed in questo modo rendere l'uso dell'XMLHttpRequest quasi del tutto superfluo .
Questo, unito all'On Demand Javascript, i feed e le API JSON, sarà l'argomento della prossima puntata. A domani martedì mercoledì (data da destinarsi)!

25 comments:

Lascia un commento


Archivio categorie

Here's the months' archive. The time is now, remember.