JSON als Alternative zu Nachrichtenübertragung auf XML-Basis
Eine interessante Alternative zu AJAX-Funktionen auf XML-Message-Basis bietet die JSON-Technologie an. Die Übertragung der Nachrichten vom Browser zum Server erfolgt dabei auf Basis einer einfachen Datenstruktur, die am besten beschrieben werden kann als Verschachtelung von Arrays und Hashes. In den meisten Programmiersprachen werden diese Strukuren nativ unterstützt, was die Verbreitung der Technologie extrem vereinfacht.
Der Overhead des durch die XML-Strukturen anfallenden Codes entfällt, somit wird die Übertragung und das Verarbeiten der Nachrichten vereinfacht. Weder auf der Seite des Clients noch auf der des Severs muss eine Transformation der Daten über XSLT oder aber ein Zusammensuchen der Daten durch aufwändiges Navigieren durch das DOM erfolgen. Zudem muss sich der Programmierer keine Gedanken um das Finden einer geeigneten XML-Struktur machen um die Daten hin- und her zu transportieren.
Eine Besonderheit ist dabei, daß die Daten auf der Client-Seite direkt als gültiger Javascript-Code vorliegen. Sie können direkt mit Javascript weiterverarbeitet werden.
JSON Nachrichtenstruktur
Die Strutur der zu übetragenden Nachrichten sind ausführlich auf www.json.org beschrieben.
Beispiel für eine JSON-Nachricht:
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": [{
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef":
"A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML", "markup"]
}]
}
}
}
Implementieren auf Client-Seite
Um die AJAX-Funktionalität auf der Client-Seite zu implementieren, benötigt man eine Javascript-Library. Eine solche ist auch über die oben genannte Seite www.json.org zu beziehen, allerdings nicht besonders leicht zu finden.
Deshalb hier die direkten Links:
Weiterhin ist eine Library HTTP.Request hilfreich, mit deren Hilfe die HTTP-Übertragung selbst Browsertransparent durchgeführt werden kann. Sie kann hier bezogen werden. Allerdings ist zu beachten, daß in der aktuellen Version dieser Library zur Übertragung immer automatisch der Content-Type application/x-www-form-urlencoded verwendet wird. Dies führt z.B. bei einem Perl-Backend auf dem Server dazu, daß der Request nicht mehr korrekt ankommt sondern nur zerteilt. Zum Beheben dieses Problems sollte die folgende Zeile in Request.js geändert werden von:
this.transport.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
in
this.transport.setRequestHeader( "Content-type", "plain/text" );
Mit diesen Vorraussetzungen kann nun der Client implementiert werden:
<script type="text/javascript" src="/global/lib/json.js"></script>
<script type="text/javascript" src="/global/lib/HTTP/Request.js"></script>
<script type="text/javascript">
var req, id = 0, since_msg = new Date();
// vor 5 Stunden
since_msg.setTime(new Date().getTime() - 1000 * 60 * 60 * 5);
function aktualisieren () {
var txt = document.getElementById('eingabe');
req = new HTTP.Request({
uri: '/test/testajax',
postbody: JSON.stringify( {
method : 'finduser',
id : id++,
params : [ txt.value, since_msg.getTime() ]
}),
onSuccess: function (trans) {
var data;
try {
var data = eval('('+trans.responseText+')'); // JSON "parsen"
} catch(e) {
alert('eval: Ungültiges JSON: ' + e);
return;
}
// hier kann ggf. noch auf data.error geprüft werden
nachrichten(data.result);
}
});
/*txt.value = ''; // Text im Eingabefeld nach Versand löschen*/
txt.focus(); // Benutzer kann weiter Text eingeben
}
function nachrichten (result) {
var ausgabe = document.getElementById('ausgabe'); // DIV fuer Text
if (result) { // Antwort auf update_box
ausgabe.innerHTML="";
for (var i = 0; i < result.length; i++) {
ausgabe.innerHTML += result[i] + "<br>\n";
}
}
}
</script>
<form action="#" onsubmit="aktualisieren();">
Ihr Text: <input type="text" id="eingabe" value="" onkeyup="aktualisieren();"/>
<input type="button" value="senden + empfangen"
onclick="aktualisieren();"/>
</form>
<div id="ausgabe"></div>
Die wichtigsten Code-Stellen:
JSON.stringify( {
method : 'finduser',
id : id++,
params : [ txt.value, since_msg.getTime() ]
}
Hier wird ein Hash der die Nachrichtenbestandteile enthält mittels JSON.stringify zu einem JSON-String umgewandelt. Die RPC-Methode, die dabei auf dem Server aufgerufen wird, nennt sich finduser. Sie bekommt die Parameter txt.value, since_msg.getTime(). id ist eine fortlaufende Nummer, die zur Identifizierung des Requests bei asynchron eintreffenden Ergebnissen dient.
Der erzeugte JSON-String zur Übertragung sieht dann so aus:
{"method":"finduser","id":0,"params":["micha",1140627355276]}
Mittels der Anweisung:
req = new HTTP.Request({
uri: '/test/testajax',
postbody: JSON.stringify( {
method : 'finduser',
id : id++,
params : [ txt.value, since_msg.getTime() ]
}),
onSuccess: function (trans) {
var data;
try {
var data = eval('('+trans.responseText+')'); // JSON "parsen"
} catch(e) {
alert('eval: Ungültiges JSON: ' + e);
return;
}
// hier kann ggf. noch auf data.error geprüft werden
nachrichten(data.result);
}
});
wird der HTTP-Request übetragen. onSuccess beinhaltet eine Funktion, die bei einem erfolgreichen Request ausgeführt wird. Hier wird die Antwort verarbeitet und dynamisch in die dargestellte Seite eingebaut.
Implementierung auf der Server-Seite
Auf der Server-Seite ist die Implementierung natürlich abhängig von der Programmiersprache der Wahl. JSON-Implementierungen sind in zahlreichen gängigen Programmiersprachen vorhanden, Links dazu sind auch über www.json.org zu finden.
Hier soll die Implementierung in Perl betrachtet werden. Wie man es von Perl gewohnt ist, steht ein fertiges CPAN-Modul JSON zur Verfügung, daß das Einbinden denkbar einfach macht. Es funktioniert (unter anderem) mit einer RPC-Schnittstelle á la SOAP. Im Idealfall muss man sich noch nicht einmal mit den JSON-Datenstrukturen beschäftigen, sondern bekommt die Parameter direkt als Funktionsparameter geliefert und gibt Perl-Datenstrukturen zurück. Die nötigen Umwandlungen passieren im Hintergrund.
package MyJSON;
use strict;
sub finduser {
my $server = shift;
my $username = shift;
my $time = shift;
#...
# return a scalar value or a hashref or an arryaref. Z.B.:
# return {'username'=>"Franz Mustermann", 'userid'=>14};
}
package main;
use strict;
use JSONRPC::Transport::HTTP;
use MyJSON;
# a la XMLRPC::Lite
JSONRPC::Transport::HTTP::CGI->dispatch_to('MyJSON')->handle();