Mittwoch, 24. Juni 2009

Nutzung von JSON Daten in GWT und GXT

Das Google Web Toolkit GWT bietet von Haus aus bereits eine relativ einfach nutzbare (c) H. Brune Möglichkeit Daten von Servern im JSON Format abzuholen. Hier soll ein kleiner Vergleich mit dem Möglichkeiten gezogen werden, die GXT bzw. Ext GWT bieten.

Wichtig ist dabei insbesondere die Frage, wie einfach JSON Daten mit Widgets verbunden werden können, da wir relativ viele Datensatzarten haben, die aber in mehr oder weniger gleichartigen Masken bearbeitet werden sollen. Die Frage, wie die Daten dann wieder an den Server zurück geschickt werden spielt hier erstmal keine Rolle.

Testvorbereitungen: Statische JSON Dateien

Um die Implementierung zu vereinfachen werden die JSON Daten im GWT über statische Dateien verfügbar gemacht. Im meinem über das Google Plugin automatisch erstellten Eclipse Projekt gibt es den /war-Ordner, der seid GWT Version 1.6 die komplette Webanwendung enthält. Für die statischen JSON Dateien wird dort einfach ein Unterordner angelegt, nennen wir ihn modell/.

Beispieldatei: /war/modell/abschluss.json

Inhalt der Datei:

[
{
id : 123,
name : Abschluss 1,
kuerzel : Abs1
},
{
id : 456,
name : Abschluss 2,
kuerzel : Abs2
},
]








Diese Datei kann im Jetty Server, der in Ecplise gestartet wird, unter http://localhost:8080/modell/abschluss.json abgerufen werden (bzw. unter dem konfigurierten Port).









JSON mit GWT









Hier nur eine ganze kurze Skizze wie dies funktioniert, die ausführliche Beschreibung findet sich in dieser GWT 1.6 Seite:













  1. JSON Quelle anlegen (haben wir schon)






  2. Stub Klasse anlegen, welche JSNI Methoden implementiert um direkt auf die Inhalte der JSON Struktur zuzugreifen. Instanzen der Stub Klasse werden über eine JSNI Methode generiert, die im Kern eval() aufruft (Sicherheitshinweise beachten!)







  3. Implementierung eines asynchronen RequestBuilder Aufrufes, der die JSON Daten holt und verarbeitet.







  4. Erzeugung einer Darstellung, z . B. mit einem Grid.











Wichtig: Der URL zur JSON Datei darf nicht GWT.getModuleBaseURL() vorangestellt werden, wie es im GWT Tutorial gezeigt wird. Es reicht eine Angabe dieser Art:









	private static final String JSON_URL = 
"/modell/abschluss.json";








Erfahrungen












  • Die Implementierung der Stub Klasse kann dann ärgerliche Mehrfacharbeit bedeuten, wenn es auf der Serverseite bereits entsprechende (Bean-)Klassen gibt. Hier lässt sich aber kein Reuse erzielen






  • Sobald man die Stub Klassen hat ist der Rest der Implementierung vergleichsweise einfach, abgesehen von der üblichen ‘Mühsal’ der Darstellung mit den relativ primitiven GWT Widgets










Quellcode








package com.chb.gxt2m3.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;

public class GXTTest implements EntryPoint {

public void onModuleLoad() {

Button btLaden = new Button("Laden");
RootPanel.get("buttons").add(btLaden);

btLaden.addClickHandler(new ClickHandler() {

public void onClick(ClickEvent event) {
refreshAbschluesse();

}
});
}

private static final String JSON_URL_ABS = "/modell/abschluesse.json";

private void refreshAbschluesse() {

RootPanel.get("display").clear();
RootPanel.get("display").add(
new Label("Abschlüsse werden geladen von " + JSON_URL_ABS));

RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
JSON_URL_ABS);

try {
builder.sendRequest(null, new RequestCallback() {

public void onError(Request request, Throwable exception) {
displayError("JSON Aufruf konnte nicht ausgeführt werden");
}

public void onResponseReceived(Request request,
Response response) {

if (200 == response.getStatusCode()) {

clearError();
abschluesseZeigen(asArrayOfAbsData(response.getText()));

} else {
displayError("Fehler beim JSON Aufruf ("
+ response.getStatusText() + ")");
}

}
});
} catch (RequestException e) {
displayError("Zugriff auf JSON gescheitert");
}
}

private void abschluesseZeigen(JsArray abs) {

RootPanel.get("display").clear();

if (abs == null || abs.length() == 0) {
RootPanel.get("display").add(
new Label("Keine Abschlüsse geladen..."));
} else {
Grid agrid = new Grid(abs.length() + 1, 3);
RootPanel.get("display").add(agrid);
agrid.setWidget(0, 0, new HTML("Id"));
agrid.setWidget(0, 1, new HTML("Kürzel"));
agrid.setWidget(0, 2, new HTML("Name"));

for (int idx = 0; idx < abs.length(); idx++) {
AbschlussData a = abs.get(idx);
agrid.setWidget(idx + 1, 0, new Label(""
+ a.getId()));
agrid.setWidget(idx + 1, 1, new Label(a.getKuerzel()));
agrid.setWidget(idx + 1, 2, new Label("" + a.getName()));
}
}
}

private void clearError() {
RootPanel.get("errorMsg").clear();
}

private void displayError(String error) {
clearError();
if (error != null)
RootPanel.get("errorMsg").add(new Label(error));
}

private final native JsArray asArrayOfAbsData(String json) /*-{
return eval(json);
}-*/;
}








JSON mit GXT









Eine Stärke von GXT (dessen Version 2 heute als Milestone 3 vorliegt) sind sicher die tollen Widgets und die Databinding Möglichkeiten, durch die sich Änderungen am zu Grunde liegenden Datenmodell an alle Teile der Anwendung melden lassen. Leider gibt es in der Demoseite nur ein Gridbeispiel mit einer XML Quelle. Etwas Suchen zeigt einem aber, dass es auch einen JsonReader gibt. Man kann das Beispiel also übertragen.









Ein Problem ist aber der Aufbau der JSON Datei. Offenbar sind verschachtelte Formate nicht möglich, zumindest kann man diesen Thread im ExtJS Forum wohl so verstehen.









Hier gibt es aber noch ein anderen Beispiel: http://extjs.net/forum/showthread.php?t=71978

Kommentar veröffentlichen