Úvod do JavaScript Object Notation (JSON) v JavaScriptu a .NET

 

Úvod do JavaScript Object Notation (JSON) v JavaScriptu a .NET

Atif Aziz, Scott Mitchell

Únor 2007

Platí pro:
   JSON
   Ajax

Shrnutí: Tento článek popisuje JavaScript Object Notation (neboli JSON), otevřený textový formát výměny dat, který poskytuje standardizovaný formát výměny dat, který je vhodnější pro webové aplikace ve stylu Ajax. (22 tištěných stránek)

Obsah

Úvod
Principy literálového zápisu v JavaScriptu
Porovnání JSON a XML
Vytváření a analýza zpráv JSON pomocí JavaScriptu
Práce s JSON v rozhraní .NET Framework
Závěr
Reference

Stáhněte zdrojový kód pro tento článek.

Úvod

Při návrhu aplikace, která bude komunikovat se vzdáleným počítačem, musí být vybrán formát dat a protokol exchange. K dispozici je celá řada otevřených standardizovaných možností a ideální volba závisí na požadavcích aplikací a již existujících funkcích. Například webové služby založené na protokolu SOAP formátuje data v datové části XML zabalené v obálce SOAP.

I když XML funguje dobře pro mnoho scénářů aplikací, má některé nevýhody, díky kterým je méně než ideální pro ostatní. Jedním z takových míst, kde je XML často méně než ideální, je u webových aplikací ve stylu Ajax. Ajax je technika používaná k vytváření interaktivních webových aplikací, které poskytují lepší uživatelské prostředí díky použití mimopásmových a jednoduchých volání na webový server místo postbacků na celou stránku. Tato asynchronní volání jsou inicializována na klientovi pomocí JavaScriptu a zahrnují formátování dat, jejich odeslání na webový server a analýzu vrácených dat a práci s vrácenými daty. I když většina prohlížečů dokáže vytvářet, odesílat a parsovat XML, JavaScript Object Notation (neboli JSON) poskytuje standardizovaný formát výměny dat, který je vhodnější pro webové aplikace ve stylu Ajax.

JSON je otevřený textový formát výměny dat (viz RFC 4627). Stejně jako XML je čitelný pro člověka, nezávislý na platformě a má širokou dostupnost implementací. Data formátovaná podle standardu JSON jsou jednoduchá a dají se neuvěřitelně snadno analyzovat implementacemi JavaScriptu, což z něj dělá ideální formát výměny dat pro webové aplikace Ajax. Vzhledem k tomu, že se jedná primárně o formát dat, není JSON omezen pouze na webové aplikace Ajax a dá se použít prakticky v jakémkoli scénáři, kdy aplikace potřebují vyměňovat nebo ukládat strukturované informace jako text.

Tento článek se zabývá standardem JSON, jeho vztahem k JavaScriptu a jeho porovnáním s XML. Probíráme jayrock, opensourcovou implementaci JSON pro .NET. Příklady vytváření a parsování zpráv JSON jsou k dispozici v JavaScriptu a C#.

Principy literálového zápisu v JavaScriptu

Literály se používají v programovacích jazycích k doslova vyjádření pevných hodnot, jako je konstantní celočíselná hodnota 4 nebo řetězec "Hello, World". Literály lze použít ve většině jazyků všude, kde je povolen výraz, například součást podmínky v řídicím příkazu, vstupní parametr při volání funkce, přiřazení proměnné atd. Například následující kód jazyka C# a Visual Basic inicializuje proměnnou x s konstantní celočíselnou hodnotou 42.

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

Různé programovací jazyky umožňují používat literály různých typů. Většina programovacích jazyků podporuje minimálně literály pro skalární typy, jako jsou celá čísla, čísla s plovoucí desetinnou čárkou, řetězce a logické hodnoty. Na JavaScriptu je zajímavé, že kromě skalárních typů podporuje také literály pro strukturované typy, jako jsou pole a objekty. Tato funkce umožňuje terse syntaxi pro vytváření a inicializaci polí a objektů na vyžádání.

Literály pole v JavaScriptu se skládají z nuly nebo více výrazů, přičemž každý výraz představuje prvek pole. Prvky pole jsou uzavřeny v hranatých závorkách ([]) a oddělené čárkami. Následující příklad definuje pole doslova se sedmi řetězcovými prvky, které obsahují názvy sedmi kontinentů:

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

Porovnejte ho s tím, jak byste vytvořili a inicializovali pole v JavaScriptu bez literálového zápisu:

  
    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";
  

Literál objektu definuje členy objektu a jejich hodnoty. Seznam členů a hodnot objektů je uzavřen do složených závorek ({}) a každý člen je oddělený čárkou. V rámci každého člena jsou název a hodnota oddělené dvojtečkami (:). Následující příklad vytvoří objekt a inicializuje ho se třemi členy s názvy Address, City a PostalCode s odpovídajícími hodnotami "123 Anywhere St.", "Springfield" a "99999".

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

Dosud uvedené příklady ilustrují použití řetězcových a číselných literálů v maticových a objektových literálech. Celý graf můžete také vyjádřit pomocí zápisu rekurzivně tak, aby elementy pole a hodnoty členů objektu mohly samy používat objektové a maticové literály. Například následující fragment kódu znázorňuje objekt, který má jako člena pole (PhoneNumbers), kde se pole skládá ze seznamu objektů.

  
    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);
}
  

Poznámka Podrobnější informace o podpoře literálů pro JavaScript najdete v příručce Core JavaScript 1.5 v části Literály.

Z javascriptových literálů na JSON

JSON je formát výměny dat vytvořený z podmnožina zápisu objektu literálu v JavaScriptu. I když je syntaxe přijímaná JavaScriptem pro hodnoty literálů velmi flexibilní, je důležité si uvědomit, že JSON má mnohem přísnější pravidla. Například podle standardu JSON musí být název člena objektu platným řetězcem JSON. Řetězec ve formátu JSON musí být uzavřený v uvozovkách. JavaScript na druhou stranu umožňuje, aby se názvy členů objektů oddělovaly uvozovkami nebo apostrofy nebo úplně vynechaly uvozovky, pokud název členu není v konfliktu s vyhrazeným klíčovým slovem JavaScriptu. Podobně je hodnota elementu pole nebo člena objektu ve formátu JSON omezená na velmi omezenou sadu. V JavaScriptu ale elementy pole a hodnoty členů objektu můžou odkazovat na prakticky jakýkoli platný javascriptový výraz, včetně volání funkcí a definic.

Kouzlo json je v jeho jednoduchosti. Zpráva formátovaná podle standardu JSON se skládá z jednoho objektu nebo pole nejvyšší úrovně. Prvky pole a hodnoty objektů mohou být objekty, matice, řetězce, čísla, logické hodnoty (true a false) nebo null. To je v kostce standard JSON! Je to opravdu tak jednoduché. Formálnější popis standardu najdete v www.json.org nebo RFC 4627 .

Jedním z bolavých bodů JSON je chybějící literál data a času. Mnoho lidí je překvapené a zklamané, že se to dozvědělo, když poprvé narazí na JSON. Jednoduché vysvětlení (nebo neutěšující) absence literálu data a času spočívá v tom, že JavaScript ho nikdy neměl: Podpora hodnot data a času v JavaScriptu je plně poskytována prostřednictvím objektu Date . Většina aplikací, které jako formát dat používají JSON, proto obvykle používá k vyjádření hodnot data a času řetězec nebo číslo. Pokud se použije řetězec, můžete obecně očekávat, že bude ve formátu ISO 8601. Pokud se místo toho použije číslo, pak hodnota obvykle znamená počet milisekund ve standardu UTC (Universal Coordinated Time) od epochy, kde je epocha definována jako půlnoc 1. ledna 1970 (UTC). Opět se jedná o pouhou konvenci, která není součástí standardu JSON. Pokud vyměňujete data s jinou aplikací, budete si muset prohlédnout její dokumentaci a zjistit, jak kóduje hodnoty data a času v literálu JSON. Například ASP.NET AJAX společnosti Microsoft nepoužívá žádnou z popsaných konvencí. Místo toho kóduje hodnoty .NET DateTime jako řetězec JSON, kde obsah řetězce je \/Date(ticks)\/ a kde značky představují milisekundy od epochy (UTC). Takže 29. listopadu 1989, 4:55:30, v UTC je zakódovaný jako "\/Date(628318530718)\/". Některé důvody pro tuto poněkud vymyšlající volbu kódování najdete v části "Inside ASP.NET AJAX's JSON date and time string" (Řetězec data a času json).

Porovnání JSON a XML

Json i XML je možné použít k reprezentaci nativních objektů v paměti v textovém formátu pro výměnu dat čitelným pro člověka. Kromě toho jsou tyto dva formáty výměny dat izomorfní – vzhledem k textu v jednom formátu je ekvivalentní jeden v druhém formátu. Například při volání některé z veřejně přístupných webových služeb Yahoo! můžete pomocí parametru řetězce dotazu určit, jestli má být odpověď formátovaná jako XML nebo JSON. Proto při rozhodování o formátu výměny dat není jednoduché zvolit jeden místo druhého jako stříbrnou odrážku, ale spíše to, jaký formát má vlastnosti , které z něj činí nejlepší volbu pro konkrétní aplikaci. Například XML má své kořeny v označování textu dokumentu a má tendenci velmi dobře zářit v tomto prostoru (jak je zřejmé u XHTML). JSON má na druhé straně své kořeny v typech a strukturách programovacích jazyků, a proto poskytuje přirozenější a pohotově dostupné mapování pro výměnu strukturovaných dat. Kromě těchto dvou výchozích bodů vám následující tabulka pomůže pochopit a porovnat klíčové charakteristiky XML a JSON.

Klíčové charakteristické rozdíly mezi XML a JSON

Charakteristika XML JSON
Typy dat Neposkytuje žádné informace o datových typech. Při přidávání informací o typu je nutné spoléhat na schéma XML . Poskytuje skalární datové typy a možnost vyjádřit strukturovaná data prostřednictvím polí a objektů.
Podpora polí Pole musí být vyjádřena konvencemi, například použitím vnějšího zástupného elementu, který modeluje obsah polí jako vnitřní prvky. Vnější prvek obvykle používá množné číslo názvu používaného pro vnitřní prvky. Nativní podpora pole
Podpora objektů Objekty musí být vyjádřeny konvencemi, často prostřednictvím smíšeného použití atributů a prvků. Podpora nativních objektů
Podpora hodnoty Null Vyžaduje použití xsi:nil na elementy v dokumentu instance XML a import odpovídajícího oboru názvů. Nativně rozpozná hodnotu null .
Komentáře Nativní podpora, která je obvykle dostupná prostřednictvím rozhraní API. Nepodporováno
Obory názvů Podporuje obory názvů, což eliminuje riziko kolize názvů při kombinování dokumentů. Obory názvů také umožňují bezpečné rozšíření stávajících standardů založených na jazyce XML. Žádný koncept oborů názvů. Kolize názvů se obvykle vyhýbají vnořením objektů nebo použitím předpony v názvu člena objektu (v praxi se dává přednost tomu prvnímu).
Rozhodnutí o formátování Složité. Vyžaduje větší úsilí při rozhodování, jak mapovat typy aplikací na elementy a atributy XML. Může vyvolat intenzivní debaty o tom, jestli je lepší přístup zaměřený na prvky nebo atributy. Jednoduché. Poskytuje mnohem přímější mapování dat aplikací. Jedinou výjimkou může být absence literálu data a času.
Velikost Velikost dokumentů bývá zdlouhavá, zejména pokud se používá formátování zaměřené na elementy. Syntaxe je velmi terse a poskytuje formátovaný text, ve kterém je většina místa spotřebována (správně) reprezentovanými daty.
Analýza v JavaScriptu Vyžaduje implementaci modelu XML DOM a další kód aplikace pro mapování textu zpět na objekty JavaScriptu. K parsování textu není potřeba žádný další kód aplikace. může používat funkci eval JavaScriptu.
Křivka učení Obecně má tendenci vyžadovat použití několika technologií v souladu: XPath, SCHÉMA XML, XSLT, OBORY NÁZVŮ XML, DOM atd. Velmi jednoduchý technologický zásobník, který už vývojáři znají se zázemím v JavaScriptu nebo jiných dynamických programovacích jazycích.

JSON je relativně nový formát výměny dat a nemá roky přechodu ani podporu dodavatele, kterou xml dnes využívá (i když JSON se rychle doháněl). Následující tabulka ukazuje aktuální stav věcí v prostorech XML a JSON.

Rozdíly v podpoře mezi XML a JSON

Podpora XML JSON
nástroje Má vyspělou sadu nástrojů široce dostupných od mnoha průmyslových dodavatelů. Podpora bohatých nástrojů – například editorů a formátovačů – je vzácná.
Microsoft .NET Framework Velmi dobrá a vyspělá podpora od verze 1.0 rozhraní .NET Framework. Podpora XML je k dispozici jako součást knihovny BCL (Base Class Library). Pro nespravovaná prostředí existuje MSXML. Zatím žádná, s výjimkou počáteční implementace v rámci ASP.NET AJAX.
Platforma a jazyk Analyzátory a formátovací nástroje jsou široce dostupné na mnoha platformách a jazycích (komerční a open source implementace). Analyzátory a formátovací nástroje jsou již k dispozici na mnoha platformách a v mnoha jazycích. Projděte si json.org, kde najdete dobrou sadu odkazů. Většina implementací v současné chvíli bývá open source projekty.
Integrovaný jazyk Dodavatelé z oboru v současné době experimentují s podporou doslova v rámci jazyků. Další informace najdete v tématu Projekt LINQ od Microsoftu . Je nativně podporován pouze v JavaScriptu nebo ECMAScriptu.

Poznámka Ani jedna tabulka nemá být úplným seznamem porovnávaných bodů. Existují další úhly, na kterých je možné porovnat oba formáty dat, ale měli jsme pocit, že tyto klíčové body by měly stačit k vytvoření počátečního dojmu.

Vytváření a parsování zpráv JSON pomocí JavaScriptu

Když jako formát výměny dat používáte JSON, dvěma běžnými úlohami je převést nativní reprezentaci a reprezentaci v paměti na její textovou reprezentaci JSON a naopak. JavaScript bohužel neposkytuje integrované funkce pro vytváření textu JSON z daného objektu nebo pole. Očekává se, že tyto metody budou zahrnuty do čtvrtého vydání standardu ECMAScript v roce 2007. Dokud nebudou tyto funkce formátování JSON formálně přidány do JavaScriptu a široce dostupné napříč oblíbenými implementacemi, použijte referenční implementační skript, který je k dispozici ke stažení na adrese http://www.json.org/json.js.

V poslední iteraci v době psaní tohoto textu json.js skript v www.json.org přidává funkce do funkce toJSONString() k polím, řetězcům, logickým typům, objektům a dalším typům JavaScriptu. Funkce toJSONString() pro skalární typy (například Number a Boolean) jsou poměrně jednoduché, protože potřebují pouze vrátit řetězcovou reprezentaci hodnoty instance. Funkce toJSONString() pro logický typ například vrátí řetězec "true", pokud je hodnota true, a "false" v opačném případě. Zajímavější jsou funkce toJSONString() pro typy Array a Object. U instancí Array je funkce toJSONString() pro každý obsažený prvek volána postupně, přičemž výsledky jsou zřetězeny čárkami, které oddělují každý výsledek. Konečný výstup uzavřený v hranatých závorkách. Podobně u instancí object je každý člen výčtu a vyvolána jeho funkce toJSONString(). Název člena a reprezentace JSON jeho hodnoty jsou zřetězeny s dvojtečku uprostřed; Název a pár hodnot každého člena je oddělen čárkou a celý výstup je uzavřen do složených závorek.

Čistý výsledek funkcí toJSONString() je, že libovolný typ lze převést do svého formátu JSON pomocí jediného volání funkce. Následující JavaScript vytvoří objekt Array a přidá sedm elementů String záměrně pomocí podrobné a ne literální metody pro ilustrativní účely. Pak se zobrazí reprezentace JSON polí:

  
    // 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

Obrázek 1: Funkce toJSONString() vygeneruje pole formátované podle standardu JSON.

Parsování textu JSON je ještě jednodušší. Vzhledem k tomu, že JSON je pouze podmnožinou javascriptových literálů, je možné ho analyzovat do reprezentace v paměti pomocí funkce , eval(expr),která se zdrojovým textem JSON považuje za zdrojový kód JavaScriptu. Funkce vyhodnocení přijímá jako vstup řetězec platného kódu JavaScriptu a vyhodnocuje výraz. V důsledku toho je k převodu textu JSON na nativní reprezentaci potřeba pouze následující jeden řádek kódu:

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

Poznámka Závorky navíc se používají, aby se se zdrojovým vstupem bezpodmínečně zacházeli jako s výrazem. To je zvlášť důležité pro objekty. Pokud se pokusíte volat eval s řetězcem obsahujícím text JSON, který definuje objekt, například řetězec "{}" (což znamená prázdný objekt), vrátí jednoduše nedefinovaný jako analyzovaný výsledek. Závorky vynutí, aby analyzátor JavaScriptu viděl složené závorky nejvyšší úrovně jako literálovou notaci pro instanci Object místo složených závorek definujících blok příkazů. Mimochodem, stejný problém nenastane, pokud je položka nejvyšší úrovně pole, jako v eval("[1;2;3]").. Kvůli jednotnosti by ale měl být text JSON před voláním eval vždy obklopen závorkou, aby nebylo možné určit, jak zdroj interpretovat.

Při vyhodnocování literálového zápisu se vrátí instance odpovídající syntaxi literálu a přiřadí se k hodnotě. Podívejte se na následující příklad, který používá funkci eval k analýze literálového zápisu pro pole a přiřazení výsledné matice k proměnným kontinentům.

  
    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.");
  

V praxi samozřejmě vyhodnocený text JSON bude pocházet z nějakého externího zdroje, a nebude tak pevně zakódován jako v předchozím případě.

Funkce vyhodnocení naslepo vyhodnocuje jakýkoliv výraz, který je předán. Nedůvěryhodný zdroj by proto mohl obsahovat potenciálně nebezpečný JavaScript spolu s literálovou notací, která tvoří data JSON, nebo do ní zamíchaný. Ve scénářích, kdy zdroj nelze považovat za důvěryhodný, důrazně doporučujeme parsovat text JSON pomocí funkce parseJSON() (nachází se v json.js):

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

Funkce parseJSON() také používá funkci eval, ale pouze v případě, že řetězec obsažený v arrayAsJSONText odpovídá textovému standardu JSON. Provede to pomocí chytrého testu regulárního výrazu.

Práce s JSON v rozhraní .NET Framework

Text JSON je možné snadno vytvořit a parsovat z kódu JavaScriptu, který je součástí jeho půvabu. Pokud se ale json používá v ASP.NET webové aplikaci, podporuje JavaScript pouze prohlížeč, protože kód na straně serveru je pravděpodobně napsaný v jazyce Visual Basic nebo C#.

Většina knihoven Ajax navržených pro ASP.NET poskytuje podporu pro programové vytváření a parsování textu JSON. Proto při práci s JSON v aplikaci .NET zvažte použití jedné z těchto knihoven. Existuje spousta opensourcových možností a možností třetích stran a Microsoft má také vlastní knihovnu Ajax s názvem ASP.NET AJAX.

V tomto článku se podíváme na příklady, které používají Jayrock, opensourcovou implementaci JSON pro rozhraní Microsoft .NET Framework, kterou vytvořil spoluvytvářovatel Atif Aziz. Rozhodli jsme se použít Jayrock místo ASP.NET AJAX ze tří důvodů:

  • Jayrock je open source, který umožňuje rozšíření nebo přizpůsobení podle potřeby.
  • Jayrock lze použít v aplikacích ASP.NET 1.x, 2.0 a Mono , zatímco ASP.NET AJAX je určen pouze pro ASP.NET verze 2.0.
  • Rozsah Jayrocku je omezený na JSON a JSON-RPC a první obor je hlavním cílem tohoto článku. I když ASP.NET AJAX zahrnuje určitou podporu pro vytváření a parsování textu JSON, jeho primárním účelem je nabídnout bohatou platformu pro vytváření komplexních webových aplikací ve stylu Ajax v ASP.NET. Další zvony a píšťalky můžou být rušivé, když se zaměřujete hlavně na JSON.

Práce s JSON v .NET pomocí Jayrocku je podobná práci s XML prostřednictvím XmlWriter, XmlReader a XmlSerializer tříd v rozhraní .NET Framework. Třídy JsonWriter, JsonReader, JsonTextWriter a JsonTextReader nalezené v Jayrocku napodobují sémantiku tříd .NET Framework XmlWriter, XmlReader, XmlTextWriter a XmlTextReader. Tyto třídy jsou užitečné pro propojení s JSON na úrovni s nízkou a streamovou orientací. Pomocí těchto tříd je možné vytvořit nebo parsovat text JSON pomocí řady volání metod. Například použití metody třídy JsonWriterWriteNumber(number) zapíše odpovídající řetězcovou reprezentaci čísla podle standardu JSON. Třída JsonConvert nabízí metody exportu a importu pro převod mezi typy .NET a JSON. Tyto metody poskytují podobnou funkčnost, jak se nachází v XmlSerializer třídy metody Serialize a Deserialize, v uvedeném pořadí.

Vytváření textu JSON

Následující kód znázorňuje použití JsonTextWriter třídy k vytvoření textu JSON pro pole řetězců kontinentů. Tento text JSON se odešle do instance TextWriter předané do konstruktoru, což je v tomto příkladu výstupní stream z konzoly (v ASP.NET můžete místo toho použít 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();
}
  

Kromě metod WriteStartArray, WriteString a WriteEndArrayjsonWriter třída poskytuje metody pro zápis dalších hodnotových typů JSON, jako je WriteNumber, WriteBoolean, WriteNull a tak dále. Metody WriteStartObject, WriteEndObject a WriteMember vytvoří text JSON pro objekt. Následující příklad znázorňuje vytvoření textu JSON pro objekt kontaktu prozkoumaný v části "Principy literálové notace v JavaScriptu":

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();        //  }
}

Metody Export a ExportToString ve třídě JsonConvert lze použít k serializaci zadaného typu .NET do textu JSON. Například místo ručního sestavení textu JSON pro pole sedmi kontinentů pomocí třídy JsonTextWriter následující volání JsonConvert.ExportToString vytvoří stejné výsledky:

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

Analýza textu JSON

Třída JsonTextReader poskytuje různé metody pro parsování tokenů textu JSON s tím základním, který je Read. Při každém vyvolání metody Read analyzátor využívá další token, kterým může být řetězcová hodnota, číselná hodnota, název člena objektu, začátek pole atd. Pokud je to možné, parsovaný text aktuálního tokenu je přístupný prostřednictvím vlastnosti Text . Pokud například čtenář sedí na logických datech, vrátí vlastnost Text "true" nebo "false" v závislosti na skutečné hodnotě analýzy.

Následující ukázkový kód používá JsonTextReader třídy k analýze prostřednictvím textové reprezentace JSON pole řetězců obsahující názvy sedmi kontinentů. Každý kontinent, který začíná písmenem "A", se odešle do konzoly:

  
    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);
        }
    }
}
  

Poznámka Třída JsonTextReader v Jayrocku je poměrně liberální analyzátor textu JSON. Ve skutečnosti umožňuje mnohem více syntaxe, než se považuje za platný text JSON podle pravidel stanovených v DOKUMENTU RFC 4627. Třída JsonTextReader například umožňuje, aby se jednořádkové a víceřádkové komentáře zobrazovaly v textu JSON tak, jak byste očekávali v JavaScriptu. Jednořádkové komentáře začínají lomítkem (//) a víceřádkové komentáře jsou lomítkem star (/*) a končí star lomítkem (*/). Jednořádkové komentáře můžou dokonce začínat znakem hash/libry (#), což je běžné u konfiguračních souborů ve stylu Unixu. Ve všech případech jsou komentáře analyzátorem zcela vynechány a nikdy se nezpřístupní prostřednictvím rozhraní API. Stejně jako v JavaScriptu jsonTextReader umožňuje oddělovat řetězec JSON apostrofem ('). Analyzátor může dokonce tolerovat další čárku za posledním členem objektu nebo prvku pole.

I se všemi těmito doplňky je JsonTextReader vyhovující parser! JsonTextWriter, na druhé straně, vytváří pouze striktní standard-vyhovující text JSON. To následuje, co se často označuje jako princip robustnosti, který říká: "Buďte konzervativní v tom, co děláte; buďte svobodní v tom, co přijímáte od ostatních."

Pokud chcete převést text JSON přímo na objekt .NET, použijte metodu importu třídy JsonConvert a zadejte výstupní typ a text JSON. Následující příklad ukazuje převod pole JSON řetězců na pole řetězců .NET:

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

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

Zde je zajímavější příklad převodu, který vezme informační kanál XML RSS, deserializuje ho na typ .NET pomocí XmlSerializer a pak převede objekt na text JSON pomocí JsonConvert (efektivně převede RSS v XML na text 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);

Poznámka Definici RichSiteSummary a související typy najdete v ukázkách, které jsou k tomuto článku přiloženy.

Použití JSON v ASP.NET

Když jsme se podívali na způsoby práce s JSON v JavaScriptu a z rozhraní .NET Framework pomocí Jayrocku, je čas přejít na praktický příklad, kde a jak je možné všechny tyto znalosti použít. Zvažte funkci zpětného volání klientského skriptu v ASP.NET 2.0, která zjednodušuje proces provádění odchozích volání z webového prohlížeče na stránku ASP.NET (nebo na konkrétní ovládací prvek na stránce). Během typického scénáře zpětného volání skript na straně klienta v prohlížeči zabalí a odešle data zpět na webový server pro určité zpracování metodou na straně serveru. Po přijetí dat odpovědi ze serveru je klient použije k aktualizaci zobrazení prohlížeče.

Poznámka Další informace najdete v článku o zpětných voláních skriptů v ASP.NET 2.0 v časopisu MSDN.

Problémem ve scénáři zpětného volání klienta je, že klient a server mohou dodávat pouze řetězec tam a zpět. Proto musí být informace, které se mají vyměňovat, před odesláním převedeny z nativní reprezentace v paměti na řetězec a potom analyzovat z řetězce zpět na jeho nativní reprezentaci v paměti při přijetí. Funkce zpětného volání klientského skriptu v ASP.NET 2.0 nevyžaduje konkrétní formát řetězce pro vyměněná data, ani neposkytuje žádné integrované funkce pro převod mezi nativní v paměti a řetězcové reprezentace; je na vývojáři, aby implementoval logiku převodu založenou na některém formátu výměny dat podle svého výběru.

Následující příklad ukazuje, jak použít JSON jako formát výměny dat ve scénáři zpětného volání klientského skriptu. Příklad se skládá z ASP.NET stránky, která používá data z databáze Northwind k poskytnutí seznamu kategorií v rozevíracím seznamu; produkty ve vybrané kategorii jsou zobrazeny v seznamu s odrážkami (viz Obrázek 3). Při každé změně rozevíracího seznamu na straně klienta se provede zpětné volání předávající pole, jehož jediným prvkem je vybrané ID kategorie.

Poznámka Předáváme pole, které jako jediný prvek obsahuje vybrané ID kategorie (nikoli pouze Id kategorie), protože standard JSON vyžaduje, aby každý text JSON měl jako kořen objekt nebo pole. Klient samozřejmě nemusí předávat na server text JSON – v tomto příkladu jsme mohli jako řetězec předat jenom vybrané ID kategorie . Chtěli jsme ale předvést odesílání textu JSON ve zprávách požadavku i odpovědi zpětného volání.

Následující kód v obslužné rutině události Page_Load konfiguruje webový ovládací prvek Categories DropDownList tak, aby při jeho změně byla volána funkce GetProductsForCategory a předána vybraná hodnota rozevíracích seznamů. Tato funkce zahájí zpětné volání klientského skriptu, pokud je hodnota předávaného rozevíracího seznamu větší než nula:

  
    // 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);
  

Metoda GetCallBackEventReference ve třídě ClientScriptManager , která se používá k vygenerování kódu JavaScriptu, který volá zpětné volání, má následující podpis:

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

Parametr argumentu určuje, jaká data se odesílají z klienta na webový server během zpětného volání, a parametr clientCallback určuje název funkce na straně klienta, která se má vyvolat po dokončení zpětného volání (showProducts). Volání metody GetCallBackEventReference vygeneruje následující kód JavaScriptu a přidá ho do vykresleného kódu:

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

[' + categoryID + ']' je hodnota, která se předá serveru během zpětného volání (pole s jedním elementem , categoryID) a showProducts je funkce JavaScriptu, která se spustí při vrácení zpětného volání.

Na straně serveru metoda, která se spouští jako odpověď na zpětné volání, používá třídu JsonConvert z Jayrocku k analýze příchozího textu JSON a formátování odchozího textu JSON. Zejména názvy produktů, které jsou přidruženy k vybrané kategorii, jsou načteny a vráceny jako pole řetězců.

  
    // 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);

Poznámka Třída JsonConvert se používá dvakrát – jednou k převodu textu JSON v eventArgument na pole celých čísel a pak k převodu pole řetězců productNames na text JSON, který se vrátí klientovi. Alternativně bychom zde mohli použít třídy JsonReader a JsonWriter , ale JsonConvert dělá stejnou práci poměrně dobře, když jsou zahrnutá data relativně malá a snadno mapovat na existující typy.

Při vrácení dat ze strany serveru javascript funkce zadané z GetCallBackEventReference metoda je volána a předána návratová hodnota. Tato metoda JavaScriptu showProducts začíná odkazováním na <element div>ProductOutput. Pak analyzuje odpověď JSON a dynamicky přidá neuspořádaný seznam s položkou seznamu pro každý prvek pole. Pokud pro vybranou kategorii nejsou vráceny žádné produkty, zobrazí se místo toho odpovídající zpráva.

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);
    }
}

Obrázek 2 znázorňuje posloupnost událostí, zatímco obrázek 3 ukazuje tento příklad v akci; kompletní kód je součástí tohoto článku ke stažení.

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

Obrázek 2: Klient odešle vybrané Id kategorie jako jeden prvek v poli a server vrátí pole přidružených názvů produktů.

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

Obrázek 3: Produkty se zobrazují v seznamu s odrážkami uvnitř vybrané kategorie.

Závěr

JSON je jednoduchý formát výměny dat založený na textu, který je založený na podmnožině literálového zápisu z programovacího jazyka JavaScript. Poskytuje stručné kódování pro struktury dat aplikací a obvykle se používá ve scénářích, kdy je implementace JavaScriptu k dispozici pro jednu nebo obě aplikace, které si vyměňují data, například ve webových aplikacích typu Ajax. Nádůstku JSON spočívá v jeho jednoduchosti při pochopení, přijetí a implementaci. JSON nemá prakticky žádnou křivku učení pro vývojáře, kteří už znají JavaScript nebo jiné programovací jazyky s podobnou podporou bohatého literálového zápisu (jako Je Python a Ruby). Parsování textu JSON v kódu JavaScriptu lze provést jednoduchým voláním funkce eval a vytvoření textu JSON je hračka s json.js skriptem, který je k dispozici na adrese http://www.json.org/json.js.

Existuje velký počet knihoven pro práci s JSON na všech hlavních platformách a architekturách. V tomto článku jsme se podívali na Jayrock, opensourcovou knihovnu pro vytváření a parsování textu JSON v aplikacích .NET. Jayrock lze použít v aplikacích ASP.NET 1.x, 2.0 a Mono. ASP.NET AJAX nabízí podobné funkce JSON, ale jenom pro aplikace ASP.NET 2.0.

Šťastné programování!

Reference

Ajax nebo AJAX?

Termín Ajax původně vytvořil Jesse James Garrett k popisu stylu webových aplikací a sady technologií, které se podílejí na vytváření vysoce interaktivních webových aplikací. V minulosti se termín Ajax šířil po webu jako zkratka AJAX, což znamená asynchronní JavaScript a XML. Časem si ale lidé uvědomili, že X v AJAX není příliš reprezentativní pro podkladový datový formát používaný ke komunikaci s webovým serverem na pozadí, protože většina implementací přechávala na JSON jako jednodušší a efektivnější alternativu. Takže místo toho, aby přišla s náhradním akronymem, jako je AJAJ, který je trochu jazyk-twister, akronym je obecně vyřazen ve prospěch Ajax místo akronymu AJAX.

V době psaní tohoto textu očekávejte, že uvidíte smíšené a široké použití "AJAX" a "Ajax" znamená jednu a tutéž věc. V tomto článku jsme se zasekli s výrazem Ajax. Komerční produkty, které poskytují architektury umožňující aplikace ve stylu Ajax, však mají tendenci používat zkratkový formulář k odlišení od podobně pojmenovaného čisticího prostředku a k tomu, aby se zabránilo případným sporům o ochranné známky nebo právním sporům.

ASP.NET AJAX: Uvnitř řetězce data a času JSON

Serializátor AJAX JSON v ASP.NET kóduje instanci DateTime jako řetězec JSON. ASP.NET AJAX během přípravných cyklů používal formát "@ticks@", kde ticks představuje počet milisekund od 1. ledna 1970 ve standardu UTC (Universal Coordinated Time). Datum a čas v UTC, například 29. listopadu 1989, 4:55:30, by byly zapsány jako "@62831853071@". I když je tento formát jednoduchý a přímočarý, nemůže rozlišovat mezi serializovanou hodnotou data a času a řetězcem, který vypadá jako serializované datum, ale není určen k deserializaci jako jeden. V důsledku toho tým ASP.NET AJAX provedl změnu konečné verze, aby tento problém vyřešil tím, že přijal formát \/Date(ticks)\/.

Nový formát se spoléhá na malý trik, který snižuje riziko nesprávné interpretace. Ve formátu JSON lze znak lomítka (/) v řetězci utéct pomocí zpětného lomítka (\), i když to není nezbytně nutné. Díky tomu tým ASP.NET AJAX upravil JavaScriptSerializer tak, aby místo toho zapisuje instanci DateTime jako řetězec \/Date(ticks)\/. Únik dvou lomítek je povrchní, ale významný pro JavaScriptSerializer. Podle pravidel " JSON je \/Date(ticks)\/" technicky ekvivalentní /"Date(ticks)/, " ale JavaScriptSerializer deserializuje první jako DateTime a druhý jako Řetězec. Pravděpodobnost nejednoznačnosti je proto ve srovnání s jednodušším formátem "@ticks@" z předběžných verzí podstatně menší.

Zvláštní poděkování

Před odesláním tohoto článku na MSDN jsme měli řadu dobrovolníků, kteří nám pomohli tento článek pro kontrolu pravopisu a poskytli nám zpětnou vazbu k obsahu, gramatikě a směru. Mezi hlavní přispěvatele procesu hodnocení patří Douglas Crockford, Eric Schönholzer a Milan Negovan.

O autorech

Atif Aziz je hlavním konzultantem ve společnosti Skybow AG, kde se primárně zaměřuje na pomoc zákazníkům pochopit a vytvářet řešení na vývojové platformě .NET. Atif pravidelně přispívá komunitě vývojářů Microsoftu tím, že přednáší na konferencích a píše články pro technické publikace. Je mluvčím INETA a prezidentem největší švýcarské uživatelské skupiny .NET. Můžete ho zastihnout na atif.aziz@skybow.com adrese nebo přes jeho web na adrese http://www.raboof.com.

Scott Mitchell, autor šesti knih o ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, školitel a spisovatel. Může být dosažitelný na mitchell@4guysfromrolla.com nebo přes jeho blog: http://ScottOnWriting.net.