AJAX válasz adattípusok: XHTML, XML, vagy JSON?

Egy AJAX alapú projekt tervezése közben merült fel a kérdés: milyen adatformátumban utazzanak az adatok a kliens és a szerver között? Ennek kapcsán megpróbálom összefoglalni a legelterjedtebb megoldásokat (XHTML, XML, JSON, SOAP) és bemutatni előnyüket, hátrányukat.

Nyilvánvaló, hogy a kérdésre ilyen általánosságban nem lehet válaszolni, a megfelelő formátum kiválasztásánál figyelembe kell venni az adott alkalmazást is. Azért, hogy a különféle módszereket könnyebb legyen összehasonlítani egy egyszerű állományrendszer listázó alkalmazást fogok elkészíteni az összes módszerrel. Az alkalmazás részleteibe nem megyek bele és nem is viszem túlzásba a vizuális megjelenítést.

Ha alaposan megvizsgáljuk az XMLHttpRequest objektum, ami az AJAX üzenetváltások alapját képezi, láthatjuk, hogy a szervertől érkező választ alapvetően két formátumban érhetjük el:

  1. a responseText változón keresztül, egyszerű karakterlánc formátumban,
  2. vagy a responseXML változón keresztül, W3C DOM objektum formájában.

Ebből adódóan egy lehetséges természetes válasz a szervertől jövő válaszra a XML dokumentum.

XML válasz

A XML válasz talán a legtermészetesebb választás, hiszen ez illeszkedik leginkább a AJAX eredeti koncepciójához. Nem meglepő tehát, hogy a legtöbb fejlesztés ebbe az irányba haladt jó ideig és a böngészők DOM értelmezőinek fejlesztését is ez motiválja.

A megvalósítandó példában a szerver egy XML dokumentumot fog visszaadni ami egy könyvárban található állományokról és könyvtárakról ad, a UNIX ls -l parancsához hasonló, értéket.

A szerver válasz egy mintája az alább látható:

<?xml version="1.0"?>
<list>
        <entry>
                <owner>szferi</owner>
                <time>2006-08-20 14:21</time>
                <name>prototype</name>
                <dir>1</dir>
                <size>4096</size>
        </entry>
        <entry>
                <owner>szferi</owner>
                <time>2006-08-20 15:36</time>
                <name>lister.py</name>
                <dir>0</dir>
                <size>806</size>
        </entry>
</list>

A válasz készítésekor szerver oldalon figyeljünk oda arra, hogy a Content-Type: text/xml legyen. Ellenkező esetben a Firefox például a responseXML értékét null-ra állítja.

A kliens oldalon a példák nagyon egyszerűek lesznek és az AJAX kezelésére a prototype.js JavaScript könyvtár ide vonatkozó részét fogom használni. A példa tisztasága érdekében a minta alkalmazásokban szándékosan nem használtam fel más megoldásokat a prototype.js-ből.

A példa program számunkra releváns részét az alábbi két függvény valósítja meg.

function getNodeValue(obj,tag) {
    return obj.getElementsByTagName(tag)[0].firstChild.nodeValue;
}
        
function xml_to_table(response) {
   var list = response.getElementsByTagName('entry')
   var t = document.createElement('table');
   for (var i=0; i < list.length; i++) {
        var tr = document.createElement('tr');
        var tags = new Array('owner', 'time', 'size', 'name');
        var dir = parseInt(getNodeValue(list[i], 'dir'));
        var td = document.createElement('td');
        if (dir == 1) {
              td.appendChild(document.createTextNode('d'))
        } else {
              td.appendChild(document.createTextNode('-'))
        }
        tr.appendChild(td);
        for (var j=0; j < tags.length; j++) {
              var td = document.createElement('td');
              td.appendChild(document.createTextNode(getNodeValue(list[i],tags[j])));
              tr.appendChild(td);
         }
         t.appendChild(tr);
    }
    document.getElementById('xml_response_out').appendChild(t);
}

Meglehetősen sok kód van itt. Jól látszik, hogy a W3 DOM függvényeket egyszerre lehet használni a válasz valamint a HTML dokumentum elemeinek hozzáférésére, módosítására. Ez így még természetesen nem túl elegáns megoldás, de működik. Világos, hogy a XSLT itt jól használható lenne, arra, hogy a beérkező XML-t rögtön XHTML részletre konvertáljuk.

Előnyök

A legelőnyösebb tulajdonsága az XML-nek, hogy azt más emberek és gépek is könnyen tudják értelmezni, olvasni. Persze nagy, bonyolult szerkezetű XML dokumentumoknál ez az előny gyorsan eltűnhet.

A másik fontos nagy előnye, hogy az XML válasz ma már szinte tetszőleges szerver oldali alkalmazástól elvárható. Ez különösen akkor érdekes lehetőség, ha mi csak a kliens oldalát valósítjuk meg mondjuk több kereskedelmi szerver alkalmazást együttesen használó felületnek.

Az XML válasz adatformátuma könnyen használható SOAP alapú web szerverek esetén is, így a szerver oldali kód ezen része újra felhasználható.

Hátrányok

Meglehetősen szószátyár módon lehet csak hozzáférni a ezzel a módszerrel, mind az adathoz, mind pedig a HTML dokumentum elemeihez. A DOM értelmezés pedig, főleg nagy adatok esetén igen teljesítmény igényes is lehet kliens és szerver oldalon is.

XHTML válasz

Ha a responseText változót használjuk, akkor lényegében tetszőleges szöveges adatstruktúrát felépíthetünk. Ezek közöl kétségkívül a legkézenfekvőbb választás, ha az adat egy XHTML darab, amint a kívánt helyre csak egy az egyben be kell illesztenünk.

A fenti példát ennek fényében változtathatjuk meg. Most a válasz valami ilyesmi lesz:

<table>
  <tr>
     <td>d</td>
     <td>szferi</td>
     <td>2006-08-20 14:21</td>
     <td>4096</td>
     <td>prototype</td>
   </tr>
  <tr>
     <td>-</td>
     <td>szferi</td>
     <td>2006-08-20 15:36</td>
     <td>806</td>
     <td>lister.py</td>
   </tr>
</table>     

Ebben az esetben hihetetlenül egyszerű az adatok feldolgozása. A kódnak a sikeres választ feldolgozó részében egy, az alábbihoz hasonló kódrészletet kell elhelyezni:

document.getElementById('output').innerHTML = t.responseText;

Előnyök

A legnagyobb előnye ennek a megoldásnak a kliens oldali egyszerűségében rejlik.

Hátrányok

Komoly problémák merülhetnek fel, ha a válasz XHTML form-ot tartalmaz. A formázási információk a szerver oldalon a kódban kell elhelyezi az eredménybe, ez komolyabb projektek esetén jelentősen megnehezíti ennek a kezelését. Számos esetben a hálózati forgalmat is jelentősen terheli felesleges formázási adatok forgalmazásával. Jelentős erőforrásokat igényelhet a szerver oldalon.

JSON

Mostanában egyre többet lehet hallani a fenti két lehetőség mellett a JSON-ról (JavaScript Object Notation), mint alternatív megoldásról. Ennek alapötlete, hogy a szerver a válaszban egy olyan karaktersorozatot küldjön, amint könnyen JavaScript objektummá alakítható. JavaScript oldalon erre a feladatra az eval() függvény való. Számos nyelvhez készült már olyan kiegészítés, aminek segítségével az adott nyelv adatstruktúrái könnyen JSON karaktersorozatokká alakíthatók és viszont. A szerver oldali mintában én Python nyelvhez készített simplejson kiegészítést használtam.

Az eredeti minta XML adat kicsit átalakítva JSON karaktersorozat formájában így néz ki (meglehetősen hasonlít az eredeti Python adathalmazra):

[{"owner" : "szferi", "time" : "2006-08-20 14:21", "name" : "prototype", dir: 1, size: 4096},
{"owner" : "szferi", "time" : "2006-08-20 15:36", "name" : "lister.py", dir: 0, size: 806}]

Ezek utána a minta alkalmazás feldolgoz függvénye így néz ki:

function json_to_table(response) {
    var list = eval('(' + response + ')');
    var t = document.createElement('table');
    for (var i=0; i < list.length; i++) {
        var tr = document.createElement('tr');
        var tags = new Array('owner', 'time', 'size', 'name');
        var dir = parseInt(list[i].dir);
        var td = document.createElement('td');
         if (dir == 1) {
              td.appendChild(document.createTextNode('d'))
         } else {
              td.appendChild(document.createTextNode('-'))
         }
         tr.appendChild(td);
         for (var j=0; j < tags.length; j++) {
             var td = document.createElement('td');
             var data = eval('list[i].' + tags[j]);
             td.appendChild(document.createTextNode(data));
             tr.appendChild(td);
          }
          t.appendChild(tr);
     }
     document.getElementById('output').appendChild(t);
}

Jól látszik, hogy a bejevő adat feldolgozásával járó XML műveletek megspóroltam, de a kimenetnél, még mindig kell ilyet végezni. Nyilván ezeket a műveleteket a prototype.js-hez hasonló könyvtárakkal tovább lehet egyszerűsíteni. Az is látszik, hogy az eval() használata nem csak a JSON adat feldolgozásánál bizonyult hasznosnak.

Előnyök

Az egyik előnyre már utaltam, az az egyszerű és erőforráskímélőbb adatfeldolgozás. A másik, nem annyira az AJAX-hoz kötődő előnye, hogy míg JavaScript-ből nem lehet különböző szerverekről jövő adatokat elérni (pl. XML, HTML), addig minden gond nélkül be lehet JavaScriptek-et importálni bárhonnan. Ha tehát JSON formátumú állományt egy adatot más szerverről importálunk, ahhoz azonnal hozzá is férhetünk. A JSON állományt, mint JavaScript-et pedig generálhatjuk akár dinamikusan is.

Lassan előnyként lehet elkönyvelni, hogy számos nagy szolgáltató (pl.: Yahoo) biztosít ilyen adathozzáférést.

Hátrányok

A legnagyobb hátránya, hogy nehezen olvasható, formátum. Ami persze csak megszokás kérdése Python és LISP programozók előnyben lesznek, amikor első ránézésre kell zárójelek összerendelését ellenőrizni.

Másik hátrányként szokták említeni, hogy nagyobb adat objektumok eval()-al történő kiértékelésekor a hibakezelésre jóval kevesebb lehetőség van, mint mondjuk az XML formátum esetén.

Végszó

Természetesen a fenti három lehetőség közel sem az összes. Számos nyelvnek létezik ún. szerializációs képessége, amivel nyelv bináris objektumait karakterláncban lehet reprezentálni. Ha egy ilyenhez készít valaki egy unszerializációs megoldást JavaScript nyelven az hasonlóan jól működhet, mint a JSON.

Sokan használják például a CSV formátumot is adattovábbításra, mivel viszonylag könnyen készíthető hozzá értelmező JavaScript nyelven is.

Egyre inkább előtérbe kerülő lehetőség a SOAP használata is, ehhez viszont számos fejlesztésre lesz szükség, mint ahogy azt egy korábbi bejegyzésemben említettem.

Az elkészült minta alkalmazások teljes kódja letölthető innen.

Referenciák

http://www.quirksmode.org/blog/archives/2005/12/the_ajax_respon.html http://techies.teamlupus.hu/2006/07/06/json/hu/ http://www.theserverside.net/tt/articles/showarticle.tss?id=Json http://blogs.ebusiness-apps.com/dave/?p=45)

Update: 20060822: Úgy látszik nem csak engem foglalkoztat a téma. Bártházi András tollából egy hasonló összefoglaló.

Szalai Ferenc | 2006, augusztus 15 - 22:07 | |
cstamas82, 2007, január 4 - 10:57

Hello!
Mostanában kezdtem el az AJAX technológiával foglalkozni. Netbeans alatt a Google Web Toolkit-et használom. Remélem tudsz segíteni:

Egy egyszerű kis alkalmazást akarok kezdetben írni, melyben a szerver oldal egy adatbázisból lekérdezés alapján adatokat küldd a kliens oldalnak ahol azt én táblázatos formában akarom megjeleníteni.
http://www.onjava.com/pub/a/onjava/2006/05/31/working-with-google-web-toolkit.html?page=1
http://www.onjava.com/pub/a/onjava/2006/05/31/working-with-google-web-toolkit.html?page=2
http://www.onjava.com/pub/a/onjava/2006/05/31/working-with-google-web-toolkit.html?page=3
http://www.onjava.com/pub/a/onjava/2006/05/31/working-with-google-web-toolkit.html?page=4
http://www.onjava.com/pub/a/onjava/2006/05/31/working-with-google-web-toolkit.html?page=5
Ezt a példát használtam alapként.

A példában a szerver a kliensnek a saját maga által készített Person osztályokból álló tömböt küldd.
A kérdésem ha én egy ilyen objektumot küldök az is xml-ként megy át? (hiszen az AJAX előnye pont ez)
Esetleg még a szerver oldalon html table vagy xml table formátumban string-ként küldjem át és a kliens oldalon a string-ből bogarásszam ki az adatokat egy táblázatba.
Segítségedet köszönöm.

cstamas82, 2007, január 4 - 15:29

Egyszerűsíteném a kérdést:
Java-ban a szerver és a kliens között 2 féle adat mehet: String vagy saját magam által készített osztály. Google Web Toolkit letudja kezelni az objektumot úgy, hogy az AJAX technológia továbbra is működjön?
Amennyiben nem és egy táblázatott valamilyen formában : XML, HTML, JSON küldök át a kliensnek van-e valamilyen szép megoldás (nem stringműveletekkel) arra, hogy a kliens oldalán bekerüljenek egy GRID vagy FLEXTABLE objektumba?

Attila (nem ellenőrzött), 2006, augusztus 27 - 19:21

engedd meg, hogy korrigaljam szakszo hasznalatodat:
1. A responseText semmikepp sem függvény, hanem tulajdonsag
2. Az XMLHttpRequest sem függvényt :-). Az egy objektum.
Gratulalok a cikkhez, jo bevezeto iras.

Szalai Ferenc, 2006, augusztus 28 - 14:43

Koszonom a korrekciot, pontositom is tustent.

zsepi (nem ellenőrzött), 2006, augusztus 21 - 10:00

A JSON hátrányainál kihagytad, hogy XSS-t tesz lehetővé, azaz nem szabad minden kívülről kapott javascript-et csak úgy eval/végrehajtani!

Garo (nem ellenőrzött), 2006, augusztus 22 - 10:30

Ez az XSS hogyan működne? Pl. van egy kérésem egy szerverről, ahonnan kapok egy JSON struktúrát. Tudom, hogy melyik szerverről mit kérek; hogyan kerülhetne be más szerverről adat? (Egy példaszerű dolgot tudnál írni? Köszi!)

zsepi (nem ellenőrzött), 2006, augusztus 22 - 12:00

valóban nem volt elég pontos, amit írtam, toxin is jelezte tegnap.

A probléma az, hogy ha csak vakon, eval-al hajtod végre a JSON-t, amit a szervertől kapsz, akkor akár egy session-lopó szkriptet is végrehajthatsz. Azaz nagyon kell bíznod a szerverben, aki a JSON-t küldi neked. Amennyiben nem (vagy akár akkor is, ha olyan extrém dolgokra gondolunk, hogy feltörték a szerverüket s ezért ártalmas kódot küldhet), nem árt, ha feldolgozás előtt ellenőrzöd a JSON tartalmat, hogy valóban az-e, amire számítasz.

Garo (nem ellenőrzött), 2006, augusztus 22 - 16:20

Igen, igen. Az XSS olyan technika, amelynél ha valamit beengedsz az oldaladra, ami más domainről származik, az ártalmasan a te szájtodra is hatással lehet. Természetesen ha scriptet használsz fel, biztosra kell menned. Az XSS technikáknak az a legveszélyesebb eleme, hogy akkor is hathatnak, amikor úgy hiszed, hogy biztosra mentél, pl. egy frame-be korlátoztad az idegen tartalmat: bizonyos trükkökkel még így is adódhat lehetősége az ártó szándékú kódnak a saját kontextusodra ráhatni.
Ellenben a JSON-nak ugyanaz az előnye is: ha akarod, "vakon" befogadhatod a külső tartalmat, ezzel extra, "önbővítő" szolgáltatáshoz juthatsz -- amennyiben megbízol a "szolgáltatóban". (Értem ez alatt, hogy a beérkezett adat/kód nem hozzáadott értéket közvetíthet, megfelelőbb formában, mint pl. az XML)
Egyébként kvázi-ártalmas "kódot" XML-ben is küldhet egy feltört szerver: mondjuk téves adatokat... amik phishingre, vagy szándékos félrevezetésre lehetnek alkalmasak!

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.