Donnerstag, 14. April 2011

Android BallApp - Iteration 3 - Einlesen der Ballliste aus einer JSON-Datei

Ziel der dritten Iteration meiner Android BallApp:

Einlesen der vollständigen Ball-Liste in die SQLite Datenbank

Die vollständige Ball-Liste verwalte ich bisher in einer Excel-Datei.

Frage 1: Wie bekomme ich die Excel-Daten in die Android-Applikation?

Die Antwort ist einfach: Die Excel-Daten werden in eine ASCII-Datei exportiert und als Resource (in Android als Assets bezeichnet) ins Projekt kopiert.

Zur Speicherung der strukturierten Ball-Daten in einer ASCII-Datei wähle ich ein JSON-Datenformat. JSON erlaubt genau wie XML die Definition einer eigenen Datenstruktur, benötigt dafür aber deutlich weniger Platz, da die schließenden Tags wegfallen. Außerdem ist JSON ein gängiges Datei-Format und kann von Android gut verarbeitet werden (wie wir später noch sehen werden).

Die JSON-Daten lassen sich mit Excel gut generieren. Auf Details dazu will ich jetzt hier nicht näher eingehen, wen das interessiert, der möge sich bitte melden. Als kleiner Tipp: die Excel-Funktion VERKETTEN hilft.

Die ASCII-Datei ballliste.json sieht wie folgt aus:

[{hrst: "3D", name: "BOF BM 2007 Catherine Massem", gr: "k", 
         l: "l", s: "7", h: "", gw: "41"},
    {hrst: "3D", name: "BOF BMM 2008 GSP Malonne", gr: "k", 
        l: "l", s: "15", h: "", gw: "42"},
    {hrst: "3D", name: "BOF DkJM 2005 Oliver Rex Wiile", gr: "k", 
        l: "l", s: "1", h: "", gw: "40"},
    ...
    {hrst: "", name: "T4", gr: "k", 
        l: "r", s: "79", h: "", gw: "30"}]

Jedes Ball-Element besteht aus einer komma-separierten Liste von Name-Wert-Paaren und wird begrenzt von geschweiften Klammern. Die eckigen Klammern am Anfang und Ende definieren die Liste der einzelnen Ball-Elemente, ebenfalls komma-separiert.

Die JSON-Datei lege ich im Verzeichnis /assets in meinem Android-Projekt ab.

Frage 2: Wie lese ich die ASCII-Datei ein?

Das Einlesen von Dateien erfolgt in Android weitestgehend analog zu Java - genauso umständlich.

Den Zugriff auf die Asset-Datei erhalte ich über den Android Context, also über meine Activity. Über die Methode getAssets() bekomme ich den AssetManager. Dieser stellt mir wiederum die Methode open(String fileName) zur Verfügung, über die ich Asset-Dateien, also Dateien die im Verzeichnis /assets abgelegt sind, öffnen kann. Ich erhalte als Ergebnis einen InputStream.

Um die Datei einigermaßen komfortabel einlesen zu können, kapsele ich den InputStream in einen InputStreamReader und diesen wiederum in einen BufferedReader. So kann ich den Inhalt der Datei zeilenweise einlesen und in einem StringBuffer sammeln.

private String readJSONBallliste(Context context) {
      StringBuffer content = new StringBuffer();
      try {
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(
                context.getAssets().open("ballliste.json")));
        String nextLine = reader.readLine();
        while (nextLine != null) {
          content.append(nextLine).append("\n");
          nextLine = reader.readLine();
        }
      } catch (IOException e) {
        Log.e(LOG_TAG, "Fehler beim Lesen der JSON-Datei", e);
      }
      return content.toString();
    }

Frage 3: Wie parse ich die JSON-Daten?

Das parsen der JSON-Daten ist in Android relativ einfach. Android bringt dafür die notwendigen Klassen im Package org.json bereits mit.

Bei meinem JSON-String handelt es sich um eine Liste von Objekten. Daher verwende ich ein JSONArray welches ich mit dem JSON-String initialisiere. Das JSONArray stellt die üblichen Methoden eines Arrays bzw. einer Liste bereit, die ich verwenden kann um durch die Liste zu iterieren. Leider implementiert das JSONArray kein Iterator oder Iterable Interface, so dass ich den altmodischen Weg gehen muss.

JSONArray jsonArray = new JSONArray(
        readJSONBallliste(context));
    for (int i = 0; i < jsonArray.length(); i++) {
      JSONObject nextBall = (JSONObject) 
          jsonArray.getJSONObject(i);
      Log.d(LOG_TAG, nextBall.getString("hrst") + 
          " - " + nextBall.getString("name"));
      ...
    }
Über die Methode getJSONObject(int index) lassen sich die einzelnen Ball-Elemente aus dem JSONArray auslesen. Das JSONObject stellt eine Reihe von Methoden zur Verfügung um zu einem Attribut-Namen den entsprechenden Wert abzufragen. Schreiben der JSON-Daten in die SQLite Datenbank Jetzt muss ich nur noch die Daten aus den JSONObjecten verarbeiten indem ich sie in die Datenbank schreibe. Die vollständige Methode dazu sieht wie folgt aus:
private void createData(SQLiteDatabase db) {
      Log.d(LOG_TAG, "Create Data...");
      String insert = "INSERT INTO " + TABLE_BAELLE + 
          " ( " + KEY_BALL_ID + ", " + KEY_BALL_FULL_NAME + 
          " ) VALUES ( ?, ? );";
      try {
        JSONArray jsonArray = new JSONArray(
            readJSONBallliste(context));
        for (int i = 0; i < jsonArray.length(); i++) {
          JSONObject nextBall = (JSONObject) 
              jsonArray.getJSONObject(i);
          Log.d(LOG_TAG, nextBall.getString("hrst") + 
              " - " + nextBall.getString("name"));
          db.execSQL(insert, new String[] { 
              String.valueOf(i), 
              nextBall.getString("name") });
        }
      } catch (JSONException e) {
        Log.e(LOG_TAG, "Fehler beim parsen der JSON-Daten", e);
      }
    }
Ich rufe die Methode beim initialen Anlegen der Datenbank auf, also in der onCreate(SQLiteDatabase db) Methode meines BallDbAdapters. Dem muss ich beim Initialisieren aus der Activity noch die Activity als Context mitgeben, damit ich Zugriff auf den AssetManager habe. Nach dem starten meiner App wird die Ball-Liste in die Datenbank geschrieben und die Liste mit den Ball-Namen wird angezeigt. Der vollständige Source Code kann wieder unter https://github.com/bobbyout/ballapp-android abgerufen werden.

Keine Kommentare: