Introduzione alla notazione dell'oggetto JavaScript (JSON) in JavaScript e .NET

 

Introduzione alla notazione dell'oggetto JavaScript (JSON) in JavaScript e .NET

Atif Aziz, Scott Mitchell

Febbraio 2007

Si applica a:
   JSON
   Ajax

Riepilogo: Questo articolo illustra javaScript Object Notation (o JSON), un formato di scambio di dati aperto e basato su testo, che fornisce un formato di scambio di dati standardizzato più adatto per le applicazioni Web in stile Ajax. (22 pagine stampate)

Contenuto

Introduzione
Informazioni sulla notazione letterale in JavaScript
Confronto tra JSON e XML
Creazione e analisi di messaggi JSON con JavaScript
Uso di JSON in .NET Framework
Conclusione
Riferimenti

Scaricare il codice sorgente per questo articolo.

Introduzione

Quando si progetta un'applicazione che comunica con un computer remoto, è necessario selezionare un formato di dati e un protocollo di scambio. Esistono diverse opzioni aperte, standardizzate e la scelta ideale dipende dai requisiti delle applicazioni e dalle funzionalità preesistenti. Ad esempio, i servizi Web basati su SOAP formattano i dati in un payload XML racchiuso in una busta SOAP.

Mentre XML funziona bene per molti scenari dell'applicazione, presenta alcuni svantaggi che lo rendono meno ideale per altri. Uno di questi spazi in cui XML è spesso meno ideale è con applicazioni Web in stile Ajax. Ajax è una tecnica usata per la creazione di applicazioni Web interattive che forniscono un'esperienza utente snappier tramite l'uso di chiamate fuori banda, leggere chiamate al server Web al posto dei postback a pagina intera. Queste chiamate asincrone vengono avviate nel client usando JavaScript e comportano la formattazione dei dati, l'invio a un server Web e l'analisi e l'uso dei dati restituiti. Anche se la maggior parte dei browser può costruire, inviare e analizzare XML, JavaScript Object Notation (o JSON) fornisce un formato di scambio dati standardizzato più adatto per applicazioni Web in stile Ajax.

JSON è un formato di scambio dati aperto basato su testo (vedere RFC 4627). Come XML, è leggibile, indipendente dalla piattaforma e gode di un'ampia disponibilità di implementazioni. I dati formattati in base allo standard JSON sono leggeri e possono essere analizzati da implementazioni JavaScript con facilità incredibile, rendendolo un formato di scambio di dati ideale per le applicazioni Web Ajax. Poiché è principalmente un formato di dati, JSON non è limitato solo alle applicazioni Web Ajax e può essere usato in qualsiasi scenario in cui le applicazioni devono scambiare o archiviare informazioni strutturate come testo.

Questo articolo esamina lo standard JSON, la relativa relazione con JavaScript e il modo in cui viene confrontato con XML. Jayrock, un'implementazione JSON open source per .NET, viene illustrata ed esempi di creazione e analisi di messaggi JSON vengono forniti in JavaScript e C#.

Informazioni sulla notazione letterale in JavaScript

I valori letterali vengono usati nei linguaggi di programmazione per esprimere letteralmente i valori fissi, ad esempio il valore intero costante pari a 4 o la stringa "Hello, World". I valori letterali possono essere usati nella maggior parte dei linguaggi ovunque sia consentita un'espressione, ad esempio parte di una condizione in un'istruzione di controllo, un parametro di input quando si chiama una funzione, nell'assegnazione di variabili e così via. Ad esempio, il codice C# e Visual Basic seguente inizializza la variabile x con il valore intero costante pari a 42.

  
    int x = 42;  // C#
Dim x As Integer = 42  ' Visual Basic
  

I diversi linguaggi di programmazione consentono valori letterali di tipi diversi. La maggior parte dei linguaggi di programmazione supporta, almeno, valori letterali per i tipi scalari come numeri interi, numeri a virgola mobile, stringhe e booleani. Ciò che è interessante su JavaScript è che, oltre ai tipi scalari, supporta anche valori letterali per tipi strutturati come matrici e oggetti. Questa funzionalità consente una sintassi terse per la creazione su richiesta e l'inizializzazione di matrici e oggetti.

I valori letterali di matrice in JavaScript sono costituiti da zero o più espressioni, con ogni espressione che rappresenta un elemento della matrice. Gli elementi della matrice sono racchiusi tra parentesi quadre ([]) e delimitati da virgole. Nell'esempio seguente viene definita una matrice letteralmente con sette elementi stringa che contengono i nomi dei sette continenti:

  
    var continents = ["Europe", "Asia", "Australia", "Antarctica", "North
 America", "South America", "Africa"];
alert(continents[0] + " is one of the " + continents.length + "
 continents.");
  

Confrontare questa procedura per creare e inizializzare una matrice in JavaScript senza la notazione letterale:

  
    var continents = new Array();
continents[0] = "Europe";
continents[1] = "Asia";
continents[2] = "Australia";
continents[3] = "Antarctica";
continents[4] = "North America";
continents[5] = "South America";
continents[6] = "Africa";
  

Un valore letterale oggetto definisce i membri di un oggetto e i relativi valori. L'elenco di membri e valori dell'oggetto è racchiuso tra parentesi graffe ({}) e ogni membro è delimitato da una virgola. All'interno di ogni membro, il nome e il valore sono delimitati da due punti (:). L'esempio seguente crea un oggetto e lo inizializza con tre membri denominati Address, City e PostalCode con i rispettivi valori "123 Anywhere St.", "Springfield" e "99999".

  
    var mailingAddress = { 
     "Address"    :   "123 Anywhere St.", 
     "City"       :   "Springfield", 
     "PostalCode" :   99999
};
alert("The package will be shipped to postal code " +
 mailingAddress.PostalCode);
  

Gli esempi presentati finora illustrano l'uso di valori letterali stringa e numerici all'interno di matrice e valori letterali oggetto. È anche possibile esprimere un intero grafico usando la notazione ricorsiva in modo che i valori degli elementi della matrice e dei membri dell'oggetto possano, a sua volta, usare valori letterali oggetto e matrice. Ad esempio, il frammento di codice seguente illustra un oggetto con una matrice come membro (PhoneNumbers), in cui la matrice è composta da un elenco di oggetti.

  
    var contact = {
     "Name": "John Doe",
     "PermissionToCall": true,
     "PhoneNumbers": [ 
       {
           "Location": "Home",
           "Number": "555-555-1234"
       },
       {
           "Location": "Work",
           "Number": "555-555-9999 Ext. 123"
       }
     ]
};
if (contact.PermissionToCall)
{
  alert("Call " + contact.Name + " at " + contact.PhoneNumbers[0].Number);
}
  

Nota Una discussione più approfondita sul supporto letterale per JavaScript è disponibile nella Guida di Core JavaScript 1.5 nella sezione Valori letterali.

Da valori letterali JavaScript a JSON

JSON è un formato di scambio dati creato da un subset della notazione dell'oggetto letterale in JavaScript. Anche se la sintassi accettata da JavaScript per i valori letterali è molto flessibile, è importante notare che JSON ha regole molto più rigorose. Secondo lo standard JSON, ad esempio, il nome di un membro dell'oggetto deve essere una stringa JSON valida. Una stringa in JSON deve essere racchiusa tra virgolette. JavaScript, d'altra parte, consente di delimitare i nomi dei membri dell'oggetto con virgolette o apostrofi o di omettere completamente la virgolette, purché il nome del membro non sia in conflitto con una parola chiave JavaScript riservata. Analogamente, un elemento matrice o un valore membro dell'oggetto in JSON è limitato a un set molto limitato. In JavaScript, tuttavia, gli elementi della matrice e i valori dei membri dell'oggetto possono fare riferimento a qualsiasi espressione JavaScript valida, incluse le chiamate e le definizioni delle funzioni.

Il fascino di JSON è nella sua semplicità. Un messaggio formattato secondo lo standard JSON è costituito da un singolo oggetto o matrice di livello superiore. Gli elementi della matrice e i valori dell'oggetto possono essere oggetti, matrici, stringhe, numeri, valori booleani (true e false) o Null. Questo, in breve, è lo standard JSON! È davvero molto semplice. Per una descrizione più formale dello standard, vedere www.json.org o RFC 4627 .

Uno dei punti più importanti di JSON è la mancanza di un valore letterale data/ora. Molte persone sono sorpreso e deluso per imparare questo quando incontrano prima JSON. La semplice spiegazione (consolante o meno) per l'assenza di un valore letterale data/ora è che JavaScript non aveva mai uno: il supporto per i valori di data e ora in JavaScript viene interamente fornito tramite l'oggetto Date . La maggior parte delle applicazioni che usano JSON come formato di dati, pertanto, in genere tendono a usare una stringa o un numero per esprimere valori di data e ora. Se viene usata una stringa, è in genere possibile aspettarlo nel formato ISO 8601. Se invece viene usato un numero, il valore viene in genere preso per indicare il numero di millisecondi nell'ora UTC (Universal Coordinated Time) dal momento in cui l'epoca viene definita come mezzanotte 1 gennaio 1970 (UTC). Anche in questo caso, si tratta di una semplice convenzione e non di parte dello standard JSON. Se si scambiano dati con un'altra applicazione, è necessario controllare la relativa documentazione per verificare come codifica i valori di data e ora all'interno di un valore letterale JSON. Ad esempio, l'ASP.NET AJAX di Microsoft usa nessuna delle convenzioni descritte. Codifica invece i valori .NET DateTime come stringa JSON, in cui il contenuto della stringa è \/Date(ticks)\/ e dove i tick rappresentano millisecondi da epoca (UTC). Quindi il 29 novembre 1989, 4:55:30, in UTC viene codificato come "\/Date(628318530718)\/". Per alcune ragioni dietro questa scelta piuttosto contriva della codifica, vedere "All'interno di ASP.NET stringa DI data e ora JSON di AJAX".

Confronto tra JSON e XML

Sia JSON che XML possono essere usati per rappresentare oggetti nativi in memoria in un formato di scambio dati, leggibile e leggibile da testo. Inoltre, i due formati di scambio dati sono isomorfici, dato il testo in un formato, un equivalente è concepibile nell'altro. Ad esempio, quando si chiama uno dei servizi Web accessibili pubblicamente di Yahoo!, è possibile indicare tramite un parametro querystring se la risposta deve essere formattata come XML o JSON. Pertanto, quando si decide su un formato di scambio dati, non è una semplice questione di scegliere uno rispetto all'altro come un punto puntino d'argento, ma piuttosto quale formato ha le caratteristiche che lo rendono la scelta migliore per un'applicazione specifica. Ad esempio, XML ha le sue radici nel contrassegno del testo del documento e tende a brillare molto bene in tale spazio (come è evidente con XHTML). JSON, d'altra parte, ha le sue radici nei tipi e nelle strutture del linguaggio di programmazione e fornisce quindi un mapping più naturale e leggibile per lo scambio di dati strutturati. Oltre a questi due punti di partenza, la tabella seguente consente di comprendere e confrontare le caratteristiche chiave di XML e JSON.

Differenze caratteristiche chiave tra XML e JSON

Caratteristica XML JSON
Tipi di dati Non fornisce alcuna nozione di tipi di dati. È necessario basarsi su XML Schema per aggiungere informazioni sul tipo. Fornisce tipi di dati scalari e la possibilità di esprimere i dati strutturati tramite matrici e oggetti.
Supporto per le matrici Le matrici devono essere espresse dalle convenzioni, ad esempio tramite l'uso di un elemento segnaposto esterno che modella il contenuto delle matrici come elementi interni. In genere, l'elemento esterno usa la forma plurale del nome usato per gli elementi interni. Supporto della matrice nativa.
Supporto per gli oggetti Gli oggetti devono essere espressi dalle convenzioni, spesso tramite un uso misto di attributi ed elementi. Supporto di oggetti nativi.
Supporto Null Richiede l'uso di xsi:nil sugli elementi di un documento di istanza XML più un'importazione dello spazio dei nomi corrispondente. Riconosce in modo nativo il valore Null .
Commenti Supporto nativo e in genere disponibile tramite le API. Non supportato.
Spazi dei nomi Supporta gli spazi dei nomi, che eliminano il rischio di conflitti di nomi durante la combinazione di documenti. Gli spazi dei nomi consentono anche l'estensione sicura degli standard basati su XML esistenti. Nessun concetto di spazi dei nomi. I conflitti di denominazione vengono in genere evitati annidando oggetti o usando un prefisso in un nome di membro dell'oggetto (il primo è preferibile in pratica).
Decisioni di formattazione Complesso. Richiede un impegno maggiore per decidere come eseguire il mapping dei tipi di applicazione a elementi e attributi XML. Può creare dibattiti riscaldati se un approccio incentrato sugli elementi o incentrato sugli attributi è meglio. Semplice. Fornisce un mapping molto più diretto per i dati dell'applicazione. L'unica eccezione può essere l'assenza di valori letterali di data/ora.
Dimensione I documenti tendono a essere lunghi, soprattutto quando viene usato un approccio incentrato sugli elementi per la formattazione. La sintassi è molto terse e restituisce testo formattato in cui la maggior parte dello spazio viene utilizzata (giustamente) dai dati rappresentati.
Analisi in JavaScript Richiede un'implementazione DOM XML e un codice dell'applicazione aggiuntivo per eseguire il mapping del testo in oggetti JavaScript. Nessun codice dell'applicazione aggiuntivo necessario per analizzare il testo; può usare la funzione eval di JavaScript.
Curva di apprendimento In genere tende a richiedere l'uso di diverse tecnologie in concerto: XPath, XML Schema, XSLT, spazi dei nomi XML, DOM e così via. Stack di tecnologie molto semplice già noto agli sviluppatori con uno sfondo in JavaScript o in altri linguaggi di programmazione dinamici.

JSON è un formato di scambio di dati relativamente nuovo e non ha gli anni di adozione o il supporto del fornitore che XML gode oggi (anche se JSON sta recuperando rapidamente). Nella tabella seguente viene evidenziato lo stato corrente degli affari negli spazi XML e JSON.

Differenze di supporto tra XML e JSON

Supporto tecnico XML JSON
Strumenti Gode di un set maturo di strumenti ampiamente disponibili da molti fornitori di settore. Il supporto avanzato degli strumenti, ad esempio editor e formattatori, è scarso.
Microsoft .NET Framework Supporto molto valido e maturo dalla versione 1.0 di .NET Framework. Il supporto XML è disponibile come parte della libreria di classi di base ( BCL). Per gli ambienti non gestiti, è presente MSXML. Nessuno finora, ad eccezione di un'implementazione iniziale come parte di ASP.NET AJAX.
Piattaforma e linguaggio I parser e i formattatori sono ampiamente disponibili in molte piattaforme e linguaggi (implementazioni commerciali e open source). I parser e i formattatori sono già disponibili in molte piattaforme e in molte lingue. Consultare json.org per un buon set di riferimenti. La maggior parte delle implementazioni per il momento tende a essere open source progetti.
Linguaggio integrato I fornitori di settore stanno attualmente sperimentando il supporto letteralmente all'interno delle lingue. Per altre informazioni , vedere Il progetto LINQ di Microsoft . È supportato in modo nativo solo in JavaScript/ECMAScript.

Nota Nessuna delle due tabelle deve essere un elenco completo di punti di confronto. Esistono altri punti su cui è possibile confrontare entrambi i formati di dati, ma si ritiene che questi punti chiave siano sufficienti per creare un'impressione iniziale.

Creazione e analisi di messaggi JSON con JavaScript

Quando si usa JSON come formato di scambio di dati, due attività comuni trasformano una rappresentazione nativa e in memoria nella relativa rappresentazione di testo JSON e viceversa. Sfortunatamente, al momento della scrittura, JavaScript non fornisce funzioni predefinite per creare testo JSON da un determinato oggetto o matrice. Questi metodi dovrebbero essere inclusi nella quarta edizione dello standard ECMAScript nel 2007. Fino a quando queste funzioni di formattazione JSON non vengono formalmente aggiunte a JavaScript e ampiamente disponibili nelle implementazioni più diffuse, usare lo script di implementazione di riferimento disponibile per il download all'indirizzo http://www.json.org/json.js.

Nella sua iterazione più recente al momento della stesura di questo articolo, lo script json.js in www.json.org aggiunge funzioni aJSONString() a matrici, string, booleane, oggetto e altri tipi JavaScript. Le funzioni toJSONString() per i tipi scalari (ad esempio Number e Boolean) sono piuttosto semplici perché devono restituire solo una rappresentazione di stringa del valore dell'istanza. La funzione toJSONString() per il tipo booleano , ad esempio, restituisce la stringa "true" se il valore è true e "false" in caso contrario. Le funzioni toJSONString() per i tipi Array e Object sono più interessanti. Per le istanze di Array, la funzione toJSONString() per ogni elemento indipendente viene chiamata in sequenza, con i risultati concatenati con virgole per delimitare ogni risultato. Output finale racchiuso tra parentesi quadre. Analogamente, per le istanze di Object, ogni membro viene enumerato e la relativa funzione toJSONString() richiamata. Il nome del membro e la rappresentazione JSON del relativo valore sono concatenati con due punti al centro; ogni coppia nome membro e valore è delimitata da una virgola e l'intero output è racchiuso tra parentesi graffe.

Il risultato netto delle funzioni toJSONString() è che qualsiasi tipo può essere convertito nel formato JSON con una singola chiamata di funzione. Il codice JavaScript seguente crea un oggetto Array e aggiunge sette elementi String usando deliberatamente il metodo dettagliato e non letterale per scopi illustrativi. Viene quindi visualizzata la rappresentazione JSON delle matrici:

  
    // josn.js must be included prior to this point

var continents = new Array();
continents.push("Europe");
continents.push("Asia");
continents.push("Australia");
continents.push("Antarctica");
continents.push("North America");
continents.push("South America");
continents.push("Africa");

alert("The JSON representation of the continents array is: " +
 continents.toJSONString());
  

Bb299886.intro_to_json01(en-us,MSDN.10).gif

Figura 1. La funzione toJSONString() genera la matrice formattata in base allo standard JSON.

L'analisi del testo JSON è ancora più semplice. Poiché JSON è semplicemente un subset di valori letterali JavaScript, può essere analizzato in una rappresentazione in memoria usando la funzione , eval(expr)considerando il testo JSON di origine come codice sorgente JavaScript. La funzione eval accetta come input una stringa di codice JavaScript valido e valuta l'espressione. Di conseguenza, la singola riga di codice seguente è tutto ciò che è necessario per trasformare il testo JSON in una rappresentazione nativa:

  
    var value = eval( "(" + jsonText + ")" );
  

Nota Le parentesi aggiuntive vengono usate per rendere eval incondizionato trattare l'input di origine come un'espressione. Ciò è particolarmente importante per gli oggetti. Se si tenta di chiamare eval con una stringa contenente testo JSON che definisce un oggetto , ad esempio la stringa "{}" (ovvero un oggetto vuoto), viene semplicemente restituito come risultato analizzato. Le parentesi forzano il parser JavaScript a visualizzare le parentesi graffe di primo livello come notazione letterale per un'istanza object anziché, ad esempio, parentesi graffe che definiscono un blocco di istruzioni. Per inciso, lo stesso problema non si verifica se l'elemento di primo livello è una matrice, come in eval("[1,2,3]").. Per uniformità, tuttavia, il testo JSON deve essere sempre racchiuso tra parentesi prima di chiamare eval in modo che non vi sia ambiguità su come interpretare l'origine.

Quando si valuta la notazione letterale, viene restituita e assegnata un'istanza corrispondente alla sintassi letterale. Si consideri l'esempio seguente, che usa la funzione eval per analizzare la notazione letterale per una matrice e assegnare la matrice risultante ai continenti delle variabili.

  
    var arrayAsJSONText = '["Europe", "Asia", "Australia", "Antarctica",
 "North America", "South America", "Africa"]';
var continents = eval( arrayAsJSONText );
alert(continents[0] + " is one of the " + continents.length + "
 continents.");
  

Naturalmente, in pratica il testo JSON valutato proviene da un'origine esterna anziché essere hardcoded come nel caso precedente.

La funzione eval valuta in modo cieco qualsiasi espressione che viene passata. Un'origine non attendibile potrebbe quindi includere JavaScript potenzialmente pericoloso insieme o misto nella notazione letterale che costituisce i dati JSON. Negli scenari in cui l'origine non può essere considerata attendibile, è consigliabile analizzare il testo JSON usando la funzione parseJSON() (disponibile in json.js):

  
    // Requires json.js
var continents = arrayAsJSONText.parseJSON();
  

La funzione parseJSON() usa anche eval, ma solo se la stringa contenuta in arrayAsJSONText è conforme allo standard di testo JSON. Esegue questa operazione usando un test intelligente delle espressioni regolari.

Uso di JSON in .NET Framework

Il testo JSON può essere facilmente creato e analizzato dal codice JavaScript, che fa parte della sua allure. Tuttavia, quando JSON viene usato in un'applicazione Web ASP.NET, solo il browser gode del supporto JavaScript, poiché il codice lato server è molto probabilmente scritto in Visual Basic o C#.

La maggior parte delle librerie Ajax progettate per ASP.NET offre supporto per la creazione e l'analisi del testo JSON a livello di codice. Pertanto, per usare JSON in un'applicazione .NET, è consigliabile usare una di queste librerie. Sono disponibili numerose opzioni open source e di terze parti e Microsoft ha anche la propria libreria Ajax denominata ASP.NET AJAX.

In questo articolo verranno esaminati esempi che usano Jayrock, un'implementazione open source di JSON per Microsoft .NET Framework creata dal coautore Atif Riceverai. Abbiamo scelto di usare Jayrock invece di ASP.NET AJAX per tre motivi:

  • Jayrock è open source, rendendo possibile estendere o personalizzare in base alle esigenze.
  • Jayrock può essere usato nelle applicazioni ASP.NET 1.x, 2.0 e Mono , mentre ASP.NET AJAX è solo per ASP.NET versione 2.0.
  • L'ambito di Jayrock è limitato a JSON e JSON-RPC e il primo è l'obiettivo principale di questo articolo. Anche se ASP.NET AJAX include il supporto per la creazione e l'analisi di testo JSON, lo scopo principale è quello di offrire una piattaforma avanzata per la compilazione di applicazioni Web di tipo Ajax end-to-end in ASP.NET. Le campane e i fischi aggiuntivi possono distrarre quando lo stato attivo principale è JSON.

L'uso di JSON in .NET con Jayrock è simile all'uso di XML tramite le classi XmlWriter, XmlReader e XmlSerializer in .NET Framework. Le classi JsonWriter, JsonReader, JsonTextWriter e JsonTextReader disponibili in Jayrock simulano la semantica delle classi .NET Framework XmlWriter, XmlReader, XmlTextWriter e XmlTextReader. Queste classi sono utili per l'interfacciamento con JSON a livello di flusso basso e orientato ai flussi. Usando queste classi, il testo JSON può essere creato o analizzato a fasi tramite una serie di chiamate al metodo. Ad esempio, l'uso del metodo writeNumber(number) della classe JsonWriter scrive la rappresentazione di stringa appropriata del numero in base allo standard JSON. La classe JsonConvert offre metodi Export e Import per la conversione tra tipi .NET e JSON. Questi metodi forniscono una funzionalità simile, come illustrato rispettivamente nei metodi della classe XmlSerializerSerialize e Deserialize.

Creazione di testo JSON

Il codice seguente illustra l'uso della classe JsonTextWriter per creare il testo JSON per una matrice di stringhe di continenti. Questo testo JSON viene inviato a un'istanza di TextWriter passata al costruttore, che si verifica come flusso di output dalla console in questo esempio (in ASP.NET è possibile usare Invece Response.Output ):

  
    using (JsonTextWriter writer = JsonTextWriter(Console.Out))
{
    writer.WriteStartArray();
    writer.WriteString("Europe");
    writer.WriteString("Asia");
    writer.WriteString("Australia");
    writer.WriteString("Antarctica");
    writer.WriteString("North America");
    writer.WriteString("South America");
    writer.WriteString("Africa");
    writer.WriteEndArray();
}
  

Oltre ai metodi WriteStartArray, WriteString e WriteEndArray , la classe JsonWriter fornisce metodi per la scrittura di altri tipi di valore JSON, ad esempio WriteNumber, WriteBoolean, WriteNull e così via. I metodi WriteStartObject, WriteEndObject e WriteMember creano il testo JSON per un oggetto. L'esempio seguente illustra la creazione del testo JSON per l'oggetto contatto esaminato nella sezione "Understanding Literal Notation in JavaScript":

private static void WriteContact()
{
    using (JsonWriter w = new JsonTextWriter(Console.Out))
    {
        w.WriteStartObject();              // {
        w.WriteMember("Name");             //   "Name" : 
        w.WriteString("John Doe");         //     "John Doe",
        w.WriteMember("PermissionToCall"); //   "PermissionToCall" :
        w.WriteBoolean(true);              //     true,
        w.WriteMember("PhoneNumbers");     //   "PhoneNumbers" :
        w.WriteStartArray();               //   [ 
        WritePhoneNumber(w,                //     { "Location": "Home",
            "Home"                         //       "Number": 
            "555-555-1234");               //         "555-555-1234" },
        WritePhoneNumber(w,                //     { "Location": "Work",
            "Work",                        //       "Number": 
            "555-555-9999");               //       "555-555-9999" }
        w.WriteEndArray();                 //   ]
        w.WriteEndObject();                // }
    }
}

private static void WritePhoneNumber(JsonWriter w, string location,
    string number)
{
    w.WriteStartObject();      //  {
    w.WriteMember("Location"); //      "Location" : 
    w.WriteString(location);   //          "...", 
    w.WriteMember("Number");   //      "Number" :
    w.WriteString(number);     //          "..."
    w.WriteEndObject();        //  }
}

I metodi Export e ExportToString nella classe JsonConvert possono essere usati per serializzare un tipo .NET specificato in testo JSON. Ad esempio, anziché compilare manualmente il testo JSON per la matrice dei sette continenti usando la classe JsonTextWriter , la chiamata seguente a JsonConvert.ExportToString produce gli stessi risultati:

string[] continents = {
      "Europe", "Asia", "Australia", "Antarctica", "North America", 
      "South America", "Africa"
};
string jsonText = JsonConvert.ExportToString(continents);

Analisi del testo JSON

La classe JsonTextReader offre un'ampia gamma di metodi per analizzare i token di testo JSON con il core da leggere. Ogni volta che viene richiamato il metodo Read , il parser utilizza il token successivo, che può essere un valore stringa, un valore numerico, un nome membro dell'oggetto, l'inizio di una matrice e così via. Se applicabile, è possibile accedere al testo analizzato del token corrente tramite la proprietà Text . Ad esempio, se il lettore è seduto sui dati booleani, la proprietà Text restituirà "true" o "false" a seconda del valore di analisi effettivo.

Il codice di esempio seguente usa la classe JsonTextReader per analizzare la rappresentazione di testo JSON di una matrice di stringhe contenente i nomi dei sette continenti. Ogni continente che inizia con la lettera "A" viene inviato alla console:

  
    string jsonText = @"[""Europe"", ""Asia"", ""Australia"", ""Antarctica"",
 ""North America"", ""South America"", ""Africa""]";

using (JsonTextReader reader = new JsonTextReader(new
 StringReader(jsonText)))
{
    while (reader.Read())
    {
        if (reader.TokenClass == JsonTokenClass.String &&
            reader.Text.StartsWith("A"))
        {
            Console.WriteLine(reader.Text);
        }
    }
}
  

Nota La classe JsonTextReader in Jayrock è un parser di testo JSON abbastanza liberale. In realtà consente molto più sintassi di quanto sia considerato testo JSON valido in base alle regole disposte in RFC 4627. Ad esempio, la classe JsonTextReader consente la visualizzazione di commenti a riga singola e a più righe all'interno del testo JSON come previsto in JavaScript. I commenti a riga singola iniziano con la barra (//) e i commenti a più righe con barra star (/*) e terminano con star barra (*/). I commenti a riga singola possono anche iniziare con il segno hash/cancelletto (#), che è comune tra i file di configurazione in stile Unix. In tutti i casi, i commenti vengono completamente ignorati dal parser e non vengono mai esposti tramite l'API. Come in JavaScript, JsonTextReader consente di delimitare una stringa JSON da un apostrofo ('). Il parser può anche tollerare una virgola aggiuntiva dopo l'ultimo membro di un oggetto o di un elemento di una matrice.

Anche con tutte queste aggiunte, JsonTextReader è un parser conforme. JsonTextWriter, d'altra parte, produce solo testo JSON conforme allo standard rigoroso. Questo segue ciò che viene spesso coniato come il principio di robustezza, che afferma: "Siate conservatori in quello che fate; essere liberale in quello che accetti dagli altri."

Per convertire il testo JSON direttamente in un oggetto .NET, usare il metodo di importazione della classe JsonConvert , specificando il tipo di output e il testo JSON. L'esempio seguente illustra la conversione di una matrice JSON di stringhe in una matrice di stringhe .NET:

string jsonText = @"[""Europe"", ""Asia"", ""Australia"", ""Antarctica"",
 ""North America"", ""South America"", ""Africa""]";

string[] continents = (string[]) JsonConvert.Import(typeof(string[]),
 jsonText);

Ecco un esempio più interessante di conversione che accetta un feed XML RSS, lo deserializza in un tipo .NET usando XmlSerializer e quindi converte l'oggetto in testo JSON usando JsonConvert (convertendo in effetti RSS in xml in testo JSON):

XmlSerializer serializer = new XmlSerializer(typeof(RichSiteSummary));
RichSiteSummary news;

// Get the MSDN RSS feed and deserialize it...

using (XmlReader reader = XmlReader.Create("https://msdn.microsoft.com/rss.xml"))
    news = (RichSiteSummary) serializer.Deserialize(reader);

// Export the RichSiteSummary object as JSON text, emitting the output to
// Console.Out.

using (JsonTextWriter writer = new JsonTextWriter(Console.Out))
    JsonConvert.Export(news, writer);

Nota La definizione di RichSiteSummary e dei relativi tipi correlati è disponibile negli esempi che accompagnano questo articolo.

Uso di JSON in ASP.NET

Dopo aver esaminato i modi per lavorare con JSON in JavaScript e dall'interno di .NET Framework usando Jayrock, è il momento di passare a un esempio pratico di dove e come applicare tutte queste conoscenze. Si consideri la funzionalità di callback dello script client in ASP.NET 2.0, che semplifica il processo di esecuzione di chiamate fuori banda dal Web browser alla pagina ASP.NET (o a un determinato controllo nella pagina). Durante uno scenario di callback tipico, lo script lato client nei pacchetti del browser e invia i dati al server Web per un'elaborazione tramite un metodo lato server. Dopo aver ricevuto i dati di risposta dal server, il client lo usa per aggiornare la visualizzazione del browser.

Nota Altre informazioni sono disponibili nell'articolo MSDN Magazine Script Callbacks in ASP.NET 2.0.

La sfida in uno scenario di callback client è che il client e il server possono solo spedire una stringa avanti e indietro. Pertanto, le informazioni da scambiare devono essere convertite da una rappresentazione nativa in memoria a una stringa prima di essere inviate e quindi analizzate da una stringa alla relativa rappresentazione in memoria nativa al momento della ricezione. La funzionalità di callback dello script client in ASP.NET 2.0 non richiede un formato stringa specifico per i dati scambiati, né fornisce alcuna funzionalità predefinita per la conversione tra le rappresentazioni native in memoria e di stringa; spetta allo sviluppatore implementare la logica di conversione in base a un formato di scambio di dati di propria scelta.

L'esempio seguente illustra come usare JSON come formato di scambio di dati in uno scenario di callback dello script client. In particolare, l'esempio è costituito da una pagina ASP.NET che usa i dati del database Northwind per fornire un elenco delle categorie in un elenco a discesa; i prodotti nella categoria selezionata vengono visualizzati in un elenco puntato (vedere la figura 3). Ogni volta che l'elenco a discesa viene modificato sul lato client, viene eseguito un callback in una matrice il cui singolo elemento è l'ID categoria selezionato.

Nota Viene passata una matrice che contiene l'elemento CategoryID selezionato come unico elemento (anziché solo CategoryID) perché lo standard JSON richiede che qualsiasi testo JSON abbia un oggetto o una matrice come radice. Naturalmente, il client non è necessario per passare il testo JSON al server. Questo esempio potrebbe essere passato solo all'ID categoria selezionato come stringa. Tuttavia, è stato necessario illustrare l'invio di testo JSON sia nei messaggi di richiesta che in quello di risposta del callback.

Il codice seguente nel gestore eventi Page_Load configura il controllo Web Categories DropDownList in modo che, quando viene modificato, viene chiamata la funzione GetProductsForCategory e passato il valore degli elenchi a discesa selezionati. Questa funzione avvia il callback dello script client se il valore dell'elenco a discesa passato è maggiore di zero:

  
    // Add client-side onchange event to drop-down list
Categories.Attributes["onchange"] = "Categories_onchange(this);";

// Generate the callback script
string callbackScript = ClientScript.GetCallbackEventReference(
    /* control        */ this, 
    /* argument       */ "'[' + categoryID + ']'", 
    /* clientCallback */ "showProducts", 
    /* context        */ "null");

// Add the Categories_onchange function
ClientScript.RegisterClientScriptBlock(GetType(),
"Categories_onchange", @"
    function Categories_onchange(sender)
    {
        clearResults();

        var categoryID = sender.value;            
        if (categoryID > 0)
        {
            " + callbackScript + @"
        }
    }", true);
  

Il metodo GetCallBackEventReference nella classe ClientScriptManager , che viene usato per generare il codice JavaScript che richiama il callback, ha la firma seguente:

  
    public string GetCallbackEventReference (
    Control control,
    string argument,
    string clientCallback,
    string context,
)
  

Il parametro argument specifica i dati inviati dal client al server Web durante il callback e il parametro clientCallback specifica il nome della funzione lato client da richiamare al completamento del callback (showProducts). La chiamata al metodo GetCallBackEventReference genera il codice JavaScript seguente e lo aggiunge al markup sottoposto a rendering:

  
    WebForm_DoCallback('__Page','[' + categoryID + 
']',showProducts,null,null,false)
  

'[' + categoryID + ']' è il valore passato al server durante il callback (una matrice con un singolo elemento, categoryID) e showProducts è la funzione JavaScript eseguita quando termina il callback.

Sul lato server, il metodo eseguito in risposta al callback usa la classe JsonConvert di Jayrock per analizzare il testo JSON in ingresso e formattare il testo JSON in uscita. In particolare, i nomi dei prodotti associati alla categoria selezionata vengono recuperati e restituiti come matrice di stringhe.

  
    // Deserialize the JSON text into an array of integers
int[] args = (int[]) JsonConvert.Import(typeof(int[]), eventArgument);

// Read the selected CategoryID from the array
int categoryID = args[0];

// Get products based on categoryID 

  NorthwindDataSet.ProductsRow[] rows = 
Northwind.Categories.FindByCategoryID(categoryID).GetProductsRows();

// Load the names into a string array
string[] productNames = new string[rows.Length];
for (int i = 0; i < rows.Length; i++)
{
    productNames[i] = rows[i].ProductName;
}

// Serialize the string array as JSON text and return it to the client
return JsonConvert.ExportToString(productNames);

Nota La classe JsonConvert viene usata due volte, una volta per convertire il testo JSON in eventArgument in una matrice di numeri interi e quindi per convertire la matrice di stringhe productNames in testo JSON per tornare al client. In alternativa, è possibile usare le classi JsonReader e JsonWriter , ma JsonConvert esegue lo stesso processo abbastanza bene quando i dati coinvolti sono relativamente piccoli e facilmente mappati ai tipi esistenti.

Quando i dati vengono restituiti dal lato server, la funzione JavaScript specificata dal metodo GetCallBackEventReference viene chiamata e passato il valore restituito. Questo metodo JavaScript, showProducts, inizia facendo riferimento all'elemento <div>ProductOutput. Analizza quindi la risposta JSON e aggiunge dinamicamente un elenco non ordinato con una voce di elenco per ogni elemento della matrice. Se non viene restituito alcun prodotto per la categoria selezionata, viene visualizzato un messaggio corrispondente.

function showProducts(arg, context)
{
    // Dump the JSON text response from the server.

    document.forms[0].JSONResponse.value = arg;
    
    // Parse JSON text returned from callback.

    var categoryProducts = eval("(" + arg + ")");

    // Get a reference to the <div> ProductOutput.
    
    var output = document.getElementById("ProductOutput");

    // If no products for category, show message.
    
    if (categoryProducts.length == 0)
    {
        output.appendChild(document.createTextNode(
            "There are no products for this category..."));
    }
    else
    {
        // There are products, display them in an unordered list. 
        
        var ul = document.createElement("ul");
        
        for (var i = 0; i < categoryProducts.length; i++)
        {
            var product = categoryProducts[i];
            var li = document.createElement("li");
            li.appendChild(document.createTextNode(product));
            ul.appendChild(li);
        }
        
        output.appendChild(ul);
    }
}

La figura 2 illustra la sequenza di eventi mentre la figura 3 mostra questo esempio in azione; Il codice completo è incluso in questo download di articoli.

Bb299886.intro_to_json02(en-us,MSDN.10).gif

Figura 2: Il client invia l'ID categoria selezionato come singolo elemento in una matrice e il server restituisce una matrice di nomi di prodotto associati.

Bb299886.intro_to_json03(en-us,MSDN.10).gif

Figura 3: I prodotti vengono visualizzati in un elenco puntato all'interno della categoria selezionata.

Conclusione

JSON è un formato di scambio di dati leggero basato su testo basato su un subset della notazione letterale dal linguaggio di programmazione JavaScript. Fornisce una codifica concisa per le strutture di dati dell'applicazione e viene in genere usata negli scenari in cui un'implementazione JavaScript è disponibile per uno o entrambe le applicazioni che scambiano dati, ad esempio nelle applicazioni Web in stile Ajax. L'allure di JSON risiede nella sua semplicità per comprendere, adottare e implementare. JSON non ha praticamente alcuna curva di apprendimento per gli sviluppatori che hanno già familiarità con JavaScript o altri linguaggi di programmazione con supporto simile per una notazione letterale avanzata (ad esempio Python e Ruby). L'analisi del testo JSON nel codice JavaScript può essere eseguita semplicemente chiamando la funzione eval e la creazione di testo JSON è una brezza con lo script json.js fornito in http://www.json.org/json.js.

Esistono numerose librerie per l'uso di JSON in tutte le principali piattaforme e framework. In questo articolo è stata esaminata Jayrock, una libreria open source per la creazione e l'analisi di testo JSON nelle applicazioni .NET. Jayrock può essere usato nelle applicazioni ASP.NET 1.x, 2.0 e Mono. ASP.NET AJAX offre funzionalità JSON simili, ma solo per le applicazioni ASP.NET 2.0.

Buon programmatori!

Riferimenti

Ajax o AJAX?

Il termine Ajax fu inizialmente coniato da Jesse James Garrett per descrivere lo stile delle applicazioni Web e il set di tecnologie coinvolte nella creazione di applicazioni Web altamente interattive. Storicamente, il termine Ajax si estende intorno al Web come acronimo AJAX, ovvero Asincrona JavaScript And XML. Con il tempo, tuttavia, gli utenti hanno capito che "X" in AJAX non era molto rappresentativo del formato di dati sottostante usato per comunicare con il server Web in background, poiché la maggior parte delle implementazioni passavano a JSON come alternativa più semplice ed efficiente. Quindi, invece di venire con un acronimo sostitutivo come AJAJ che è un po 'di twister lingua, l'acronimo viene in genere ritirato a favore di Ajax il termine anziché AJAX l'acronimo.

Al momento di questa scrittura, aspettatevi di vedere un uso misto e ampio di "AJAX" e "Ajax" per significare una e la stessa cosa. In questo articolo abbiamo bloccato con "Ajax il termine". I prodotti commerciali che forniscono framework che consentono applicazioni in stile Ajax, tuttavia, tendono a usare il modulo di acronimo per distinguere da un prodotto agente di pulizia denominato analogamente e per evitare eventuali potenziali controversie legali o marchi.

ASP.NET AJAX: all'interno della stringa di data e ora JSON

Il serializzatore JSON AJAX in ASP.NET codifica un'istanza di DateTime come stringa JSON. Durante i cicli di pre-rilascio, ASP.NET AJAX ha usato il formato "@ticks@", dove i tick rappresentano il numero di millisecondi dal 1° gennaio 1970 in Universal Coordinated Time (UTC). Una data e un'ora UTC come il 29 novembre 1989, 4:55:30 AM verrà scritta come "@62831853071@". Anche se semplice e semplice, questo formato non può distinguere tra un valore di data serializzato e ora e una stringa simile a una data serializzata, ma non deve essere deserializzata come una. Di conseguenza, il team ASP.NET AJAX ha apportato una modifica per la versione finale per risolvere questo problema adottando il formato "\/Date(ticks)\/".

Il nuovo formato si basa su un piccolo trucco per ridurre la possibilità di un'interpretazione errata. In JSON è possibile eseguire l'escape di un carattere in una stringa con una barra rovesciata (/\) anche se non è strettamente richiesto. Sfruttando questo scopo, il team di ASP.NET AJAX ha modificato JavaScriptSerializer per scrivere un'istanza dateTime come stringa "\/Date(ticks)\/". L'uscita delle due barre in avanti è superficiale, ma significativa per JavaScriptSerializer. Per regole JSON, "\/Date(ticks)\" / è tecnicamente equivalente a "/Date(ticks)/" ma JavaScriptSerializer deserializzatore l'ex come DateTime e quest'ultimo come stringa. Le probabilità di ambiguità sono quindi notevolmente inferiori rispetto al formato "@ticks@" più semplice dalle versioni precedenti.

Grazie speciali

Prima di inviare questo articolo a MSDN, abbiamo avuto un numero di volontari che aiutano a leggere l'articolo e fornire commenti e suggerimenti sul contenuto, la grammatica e la direzione. I collaboratori primari al processo di revisione includono Douglas Crockford, Eric Schönholzer e Milano Negovan.

Informazioni sugli autori

Atif Aziz è un consulente principale presso Skybow AG, dove la sua principale attenzione è aiutare i clienti a comprendere e creare soluzioni sulla piattaforma di sviluppo .NET. Atif contribuisce regolarmente alla community degli sviluppatori Microsoft parlando in conferenze e scrivendo articoli per pubblicazioni tecniche. È un relatore INETA e il presidente del più grande gruppo di utenti .NET svizzero. Può essere raggiunto all'indirizzo atif.aziz@skybow.com o tramite il suo sito Web all'indirizzo http://www.raboof.com.

Scott Mitchell, autore di sei libri ASP/ASP.NET e fondatori di 4GuysFromRolla.com, ha lavorato con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, allenatore e scrittore. Può essere raggiunto all'indirizzo mitchell@4guysfromrolla.com o tramite il suo blog: http://ScottOnWriting.net.