Witam w kolejnym wpisie z cyklu “egzamin MCSD: 70-480”! W odcinku dzisiejszym przedstawię sposoby na konsumowanie danych JavaScript. Zgodnie z tym co napisane jest na temat wymagań na stronach Microsoftu, w poście niniejszym powinniśmy zająć się takimi zagadnieniami jak konsumowanie danych w formacie JSON oraz XML; pobieranie danych z webservice’ów; ładowanie danych z innych źródeł za pomocą obiektu ‘XMLHTTPRequest’. Postaram się więc poruszyć te zagadnienia podpierając się przykładami kodu. Zapraszam jak zwykle do lektury!

Konsumowanie danych JavaScript w formacie JSON oraz XML

Temat użycia formatu JSON przy wywołaniach asynchronicznych (za pomocą jQuery) był już w pewnym stopniu przeze mnie poruszany w poście na temat “callback’ów” w JavaScript, dlatego tutaj przyjrzymy się bliżej samemu formatowi JSON oraz zapoznamy się ze sposobem na parsowanie danych w formacie JSON oraz XML za pomocą standardowych metod JavaScript.

JSON

Zacznijmy od JSON’a. Format ten jest otwartym, opartym na tekście sposobem wymiany danych. Tak jak XML, jest łatwy do czytania przez człowieka, niezależny od platformy, na której jest stosowany pozwala na szeroką możliwość implementacji. Dane przygotowane zgodnie z formatem JSON, są mniej “zasobożerne” niż te przesyłane za pomocą XML’a i mogą być w bardzo łatwy sposób parsowane za pomocą języka takiego JavaScript. Z tego powodu, format ten jest szeroko stosowany przy komunikacji asynchronicznej (AJAX) - oczywiście, to tylko jedno z zastosowań tego formatu: może być on stosowany w każdej sytuacji, w której wymagane jest przechowywanie i wymiana danych w postaci tekstu.

Przykład

Dostęp do danych w formacie JSON z poziomu języka JavaScript jest łatwa, ponieważ notacja JSON’a podobna jest składniowo do notacji JavaScript’u. Spójrzmy na przykład danych w formacie JSON (za wikipedią):

{
  "menu": {
    "id": "file",
    "value": "File",
    "popup": {
      "menuitem": [
        { "value": "New", "onclick": "CreateNewDoc()" },
        { "value": "Open", "onclick": "OpenDoc()" },
        { "value": "Close", "onclick": "CloseDoc()" }
      ]
    }
  }
}

Jak widać, wygląda to podobnie do JavaScriptowej definicji inline obiektu, wraz z definicją jego właściwości i ich wartości.

Użycie funkcji parse

Konsumpcja/parsowanie danych w formacie JSON możliwa jest poprzez zastosowanie metody ‘parse’ obiektu ‘JSON’ lub za pomocą funkcji ‘eval’. Oczywiście użycie metody ‘parse’ jest bezpieczniejsze ponieważ funkcja ‘eval’ może być wykorzystana do wywołania dowolnego kodu, który może być na przykład poleceniem JavaScript. Zastosowanie funkcji ‘JSON.parse’ dokonuje sprawdzenia czy podany ciąg znaków jest prawidłowy i tylko wówczas możliwe jest przekształcenie tekstu na obiekt danych. Spójrzmy na przykład:

var jsonText = '{"menu": { "id": "file", "value": "File", "popup": { "menuitem": [{"value": "New", "onclick": CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"}]}}}';

var jsonObj = JSON.parse(jsonText);

alert('id: ' + jsonObj.menu.id); // zwraca 'id: file'
alert('value: ' + jsonObj.menu.value); // zwraca 'value: File'
alert('popup: ' +
      jsonObj.menu.popup.menuitem[0].value +
      ', onclick: ' +
      jsonObj.menu.popup.menuitem[0].onclick); // zwraca 'popup: New', onclick: CreateNewDoc'

Powyższy kod prezentuje użycie metody ‘JSON.parse’, w celu przetłumaczenia danych w formacie JSON (zawartych w zmiennej ‘jsonText’). Widać więc, w jaki sposób w obiekcie ‘jsonObj’ odwzorowane są dane z poprzedniego przykładu. Mamy do nich dostęp poprzez odpowiednie właściwości, a ich struktura w pełni oddaje strukturę danych JSON. Podobny efekt uzyskalibyśmy przy użyciu metody ‘eval’, dlatego przykład jej użycia pominę. Powyższy przykład można poklikać na jsfiddle.net.

XML

To za sadzie wszystko na temat parsowania JSON’a, przejdźmy więc do parsowania danych w formacie XML. Na początek dane wejściowe (te same co w przypadku JSON’a):

<menu id="file" value="File">
    <popup>
        <menuitem value="New" onclick="CreateNewDoc()" />
        <menuitem value="Open" onclick="OpenDoc()" />
        <menuitem value="Close" onclick="CloseDoc()" />
    </popup>
</menu>

Kod parsujący taki XML mógłby wyglądać następująco:

// splaszczony tekst z poprzedniego przykladu
var xmlText = '<menu id="file" value="File"><popup><menuitem value="New" onclick="CreateNewDoc()" /><menuitem value="Open" onclick="OpenDoc()" /><menuitem value="Close" onclick="CloseDoc()" /></popup></menu>';

if (window.DOMParser) { // inne przegladarki
    var xmlDoc = DOMParser().parseFromString(xmlText, 'text/xml');
} else { // stare wersje Internet Explorer
    var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
    xmlDoc.async = false;
    xmlDoc.loadXML(xmlText);
}

alert(xmlDoc.documentElement.nodeName); // zwraca 'menu'
alert(xmlDoc.documentElement.childNodes[0].nodeName); // zwraca 'popup'

Widać od razu, że w tym przypadku nakład pracy jest dużo większy. Nie dość, że musimy obsłużyć dwie metody parsowania danych XML (stare wersje Internet Explorer nie potrafią tego zrobić w sposób standardowy), to późniejsze użycie sparsowanego już obiektu, również nie jest specjalnie wygodne. Nie ma się więc co dziwić popularności formatu JSON.

Pobieranie danych z WebService’ów

Konsumowanie danych JavaScript z WebService’ów można zrealizować na kilka sposobów. Jednym z nich jest zastosowanie metody ‘ajax’ dostępnej w bibliotece jQuery. Poniżej przykład:

$.ajax({
    type: 'POST',
    url: 'http://localhost:8080/WebService.asmx/GetData',
    data: { 'userId' : '174', 'caseId' : '15' },
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function(message) {
        $('#container').html(message.userName);
    },
    error: function(e){
        $('#container').html('Dane niedostępne');
    }
});

Powyższy kod, że jako parametr wywołania metody ‘ajax’, przekazuje się obiekt, w którym ustawiamy odpowiednie właściwości. I tak, właściwość ‘type’ służy do określenia typu żądania (POST lub GET). Do właściwości ‘url’ przypisujemy adres URL odpowiedniej akcji na serwerze - w tym przypadku jest to adres metody ‘GetData’ webservice’u ‘WebService.asmx’. Właściwość ‘data’ pozwala przekazać nam parametry wywołania metody webservice’e (przekazujemy parametry ‘userId’ oraz ‘caseId’). Dwa następne parametry, ‘contentType’ oraz ‘dataType’ służą do określenia formatu zwracanych danych - jak widać, metoda ‘GetData’ zwraca dane w formacie JSON (możliwe jest oczywiście również odbieranie danych w formacie XML). Kolejne właściwości pozwalają nam na zdefiniowanie funkcji “callback”, dzięki czemu możemy wykonać odpowiednie operacje ze zwróconymi danymi (właściwość ‘success’) lub obsłużyć sytuację gdy coś pójdzie nie tak (właściwość ‘error’).

XMLHTTPRequest

Innym sposobem na wywołanie metody WebService’u jest użycie obiektu XMLHTTPRequest. Obiekt ten może zostać użyty do przetwarzania zarówno plików XML dostępnych na zdalnym serwerze jak i do konsumowania metod WebService’u zwracających dane w formacie XML (można też przetwarzać dane innych typów, np. JSON czy binarne - aby to zrobić, należy określić zwracany typ za pomocą właściwości ‘responseType’ obiektu ‘XMLHttpRequest’). Spójrzmy na przykład:

var request = new XMLHttpRequest();

if (window.XMLHttpRequest) { // w starych IE niedostepne
    var url = 'http://localhost:8080/WebService.asmx/GetData';

    // callback wywolywany gdy status 'readyState' sie zmienia
    request.onreadystatechange = function() {
        if (request.readyState == 4) { // operacja zakonoczna
            $('#container').html(request.responseXML.xml);
        }
    };

    request.open(&quot;GET&quot;, url, true); // true = wywolanie asynchroniczne
    request.send();
}

Jak pokazuje powyższy kod, użycie obiektu ‘XMLHttpRequest jest mniej wygodne od zastosowania metody ‘ajax’. Pomijając fakt, że nie obsługują go stare przeglądarki Internet Explorer (w ich przypadku trzeba skorzystać z obiektu ‘ActiveXObject’), mamy też dużo mniejszą możliwość konfiguracji wywołania. Dlatego ze swojej strony mogę polecić stosowanie jQuery do przeprowadzania takich operacji.

W powyższym przykładzie, w linii trzeciej mamy sprawdzenie czy przeglądarka obsługuje obiekt ‘HMLHtmlRequest’ - jeśli nie, można utworzyć go za pomocą wywołania ‘new ActiveXObject(“MSXML2.XMLHTTP.3.3”)’. W kolejnych liniach mamy konfigurację adresu URL metody WebService’u. Następnie definiujemy obsługę zdarzenia ‘readystatechange’ (to oczywiście jeden ze sposobów, innym jest obsłużenie zdarzenia ‘load’) - wewnątrz sprawdzamy czy status ‘readyState’ ma wartość ‘4’ - jeśli tak, znaczy to że operacja pobierania danych została zakończona. W tym miejscu należy zwrócić uwagę na dostęp do danych. Dobieramy się do nich za pomocą właściwości ‘responseXML’. W Internet Explorerach starszych niż wersja 10, trzeba dodatkowo dokonać parsowania zwróconej wartości (tak jak to zostało pokazane we wcześniejszych przykładach). Na koniec przykładu, wołamy metodę ‘open’, podając typ wywołania, adres URL oraz informację czy wywołanie ma być asynchroniczne. Wywołanie metody ‘send’ powoduje faktyczny “request” do WebService’u.

Podsumowanie

Na podstawie przedstawionych przykładów, wysnuć można wniosek, że do komunikacji pomiędzy serwerem czy WebService’em najlepiej nadaje się format JSON, a konsumowanie danych JavaScript, które otrzymójemy w ten sposób najlepiej przeprowadzić jest za pomocą bibliotek takich jak jQuery. Można w ten sposób mieć dużo większą kontrolę na tym co się robi, nie mówiąc że jest to sposób bardziej elegancki ;)

Na dziś to wszystko, zapraszam na kolejny wpis, tym razem na temat serializacji, deserializacji i transmisji danych.