XML in Processingmit Hilfe von XMLElement

Kern der Lesson ist die Erläuterung des XML-Dateiformats und dessen Verwendung in Processing mit Hilfe der standard Processing XML-Bibliothek (XMLElement). Praktisches Beispiel ist das Einlesen von XML-basierten Daten aus dem Musik-Service Last.fm.

Viele Sketchen wurden in den vergangenen Abschnitten mit Daten versorgt. Neben den klassischen Eingabeformen von Maus und Tastatur gaben wir Aufschluss über das Auslesen von Textdateien und die Anbindung der Kamera. All diese Formen des 'inputs' lassen eine Steuerung des sog. Programmablaufs und das erzeugte Resultat zu. Um komplexere Systeme in die Applikation einzubeziehen, zeigen wir in diesem Teil wie man mittels Processing mit etablierten Datenformaten umgeht.

XML

Als eine Erweiterung des einfachen Auslesen eines Textdokumentes bietet sich das XML-Format (Extensible Markup Language) an. Dabei handelt es sich um reine Textdateinen (z.B. .txt) die ohne jegliche Textformatierung mit der Dateiendung .xml abgelegt werden. Speziell im Internet hat sich XML als Standard für das Austauschen von Inhalten durchgesetzt. Sämtliche im Netz zugängliche APIs bieten auf Anfrage Inhalte an, die Grundlage für regelbasierte Visualiserungen seien können.
Neben von Webservices generierten Dokumenten kann bei Bedarf auf selbst erstellte XMLs zugegriffen werden. Mit jedem Texteditor der das unformatierte Abspeichern von Textdateien unterstützt (Textmate, jEdit, Dreamweaver).

Struktur von XML Dokumenten

Innerhalb des Dokuments sind die eigentlichen Daten in logischen Strukturen verpackt. Dieses Gerüst, auch 'Markup' genannt, erlaubt eine hierarchisch Strukturierung von Informationen. Mit einer Klammern-Schrägstrich-Kombination <> & werden Tags formuliert, die als Grundbausteine der Struktur fungieren. Tags sind für die Zugänglichkeit im Programm eindeutig benannt und treten immer paarweise auf(auch als Knoten bezeichnet). Der Name des öffnenden Tags muss dabei dem des schließenden genau entsprechen. Dies ist eine der wenigen Regeln die beim Anlegen von XMLs zu beachten ist. Der Name der Tags ist nicht vorgegeben und kann von uns festgelegt werden. Vorsicht ist jedoch vor Sonderzeichen im Namen geboten.
Zwischen einem öffnendem und einem schließenden Tag wird die eigentliche Information platziert. Der Name des Tags sollte möglichst kurz und seinem Inhalt entsprechend gewählt sein, um auch anderen das Verstehen des Dokuments zu erleichtern.
<regal>Bücher</regal>
Diese Ablage der Information 'Bücher' hat keinen Vorteil im Vergleich zum einfachen Textdokument aufzuweisen. In der aktuellen Form haben wir haben einen Inhalt in einem XML-Dokument abgelegt. Ein Eigenschaft von Knoten ist neben dem Halten von Inhalten das Insichtragen von weiteren Knoten. Durch das Verschachteln erhält man die oben kurz angesprochene Strukturierung in Hierarchien.
<regal>
    <buch>Visualizing Data</buch>
    <buch>Type-One</buch>
</regal>
In diesem Schritt haben wir drei Informationen abgelgt. Die Anzahl der sich im Regal befindenden Bücher, und welchen Namen diese tragen. Wenn Knoten verschachtelt werden, muss auf die Reihenfolge der öffnenden und schließenden Tags geachtet werden. Der Hauptknoten, in dem Fall 'regal', kann nicht geschlossen werden bis der letzte Unterknoten ('buch') mit einem Tag beendet wurde. Anderenfalls kann die Struktur der XML nicht eindeutig von Processing konstruiert und damit nicht ausgelesen werden.
Neben dem Füllen von Knoten mit Inhalten erlaubt ein zweiter Weg Informationen innerhalb der Struktur zu platzieren.
<regal>
    <buch seiten="367">Visualizing Data</buch>
    <buch seiten="244">Type-One</buch>
</regal>
Sogenannte Attribute können im öffnenden Tag nach dem Namen platziert werden, um den Knoteninhalt detailierter zu beschreiben. Ein Attribut setzt sich dabei immer aus einem Attributsnamen und einem Wert in doppelten Anführungszeichen zusammen. In dem öffnenden Tag eines Knotens dürfen mehrere Attribute vorkommen - diese müssen sich aber vom Namen unterscheiden, keine Doppelnennung.

XML in Processing

Processing bringt für das Verarbeiten von XML Dokumenten die XMLElement Klasse mit. Durch sie wird beim Aufruf des Konstruktors die XML Datei geladen und zum Auslesen/Bearbeiten eingelesen. Der Funktionsumfang der Klasse gliedert sich in die Bereiche 'Struktur' und 'Auslesen'. Bereich Nummer eins widmet sich dem Navigieren und Selektieren von Abschnitten in der Datenstruktur, Nummer zwei dem Extrahieren von Informationen. Da grundsätzlich alle Informationen in der XML durch Text repräsentiert wird, gibt es spezielle Funktionen für das Auslesen von Attributen, welche den XML Inhalt an dieser Position in unterschiedliche Datentypen konvertieren können.
Weiterhin werden von der Processing Community vergleichbare Werkzeuge zum Arbeiten mit XMLs angeboten. Eine der bekanntesten ist proXML von Christian Riekoff. Sie bietet im Vergleich zum XMLElement von Processing mehr Funktionalität und ist bei größeren Projekten sehr zu empfehlen.
Struktur
  • getChildCount() liefert die Anzahl der Kinden (Subknoten), welche sich innerhalb eines Knotens befinden. Dieser Wert wird bei der Formulierung einer for-Schleife (zum Durchlaufen aller Knoten) als Grenze in der Bedingung gesetzt. siehe Referenz
    int numChildren = xml.getChildCount();
  • getChild() gibt einen spezifischen Subknoten aus der XML Struktur als XMLElement zurück. Dabei kann der numerische Index, die Position in der Liste der Kinder, oder der Pfad zum Element angegeben werden. siehe Referenz
    XMLElement child = xml.getChild (1);
  • getChildren() gibt alle Kinder eines Knotens in Form eines Arrays zurück. Optional kann ein Pfad zu einem Bestimmten Zweig in der Hierarchie angegeben werden. Dann sind nur Subknoten im Resultat enthalten die diesem entsprechen. siehe Referenz
    XMLElement[] children = xml.getChildren ("city/berlin");
  • getName() liefert den Tagnamen des Knotens zurück. siehe Referenz
    println (xml.getName ());
Auslesen
  • getContent() gibt den Inhalt eines Knotens zurück. siehe Referenz
    println (xml.getContent ());
  • getIntAttribute() gibt den Inhalt eines Attributes als Ganzzahl zurück. siehe Referenz
    int num = xml.getIntAttribute ("anzahl");
  • getFloatAttribute() gibt den Inhalt eines Attributes als Fließkommazahl zurück. siehe Referenz
    float mass = xml.getFloatAttribute ("gewicht");
  • getStringAttribute() gibt den Inhalt eines Attributes als Zeichenkette (String) zurück siehe Referenz
    String surname = xml.getStringAttribute ("name");

Arbeiten mit Daten

Die folgenden Beispiele zeigen in Grundzügen den Umgang mit XML Dateien in Processing. Wie bei Arrays ist die for-Schleife das Werkzeug um Knoten für Knoten durch das Dokument zu laufen und sich deren Inhalte zu bedienen.
Bsp.: Knoteninhalte
Zum Einsteigen beschäftigt sich dieses Beispiel mit einer simplen XML Struktur. In einem 'Mutter'-Knoten befinden sich drei Knoten, die jeweils einen Künstlernamen enthalten. Durch das Laden des Dokuments in die Variable xml kann die Abfrage getChildCount() getätigt werden. Als Resultat erhält man die Anzahl der Subknoten - die Anzahl der Künstler. In einer for-Schleife nehmen wir uns jedem Knoten an und lesen den Inhalt mit getContent() aus. Final wir dieser durch den text()-Befehl im Sketch abgebildet.
XMLElement xml;
PFont font;

size(320, 240);
background(0);
smooth();

// Laden der Schrift auf dem 'data' Ordner
font = loadFont ("RockwellStd-48.vlw");
textFont (font);
textSize (40);

// Zeilenhöhe
int leading = 55;
// Laden der XML
xml = new XMLElement(this, "music.xml");
// Wieviele Knoten hat die XML?
int numNodes = xml.getChildCount();

// Für jeden Knoten
for (int i = 0; i < numNodes; i++) {
  // Auslesen des Konoteninhalts
  XMLElement kid = xml.getChild(i);
  String artist = kid.getContent();
  // Anzeigen im Sketch
  text(artist, 20, 20 + leading * (i + 1));
}
<?xml version="1.0" encoding="utf-8"?>
<artists>
    <artist>Junior Boys</artist>
    <artist>Hot Chip</artist>
    <artist>Britney Spears</artist>
</artists>
Bsp.: Attribute
Neben dem Namen des Künstlers ist nun das Gengre im 'artist'-Knoten enthalten. Diese Information erreicht man durch das Aufrufen von getStringAttribute(), mit Angabe des Attributnamens. Genau wie beim Auslesen des Knoteninhalts beziehen wir uns dabei auf das XMLElement kid, welches innerhalb der for-Schleife jeweils einen Subknoten repräsentiert.
XMLElement xml;
PFont font;

size (320, 240);
background (0);
smooth ();

// Laden der Schrift aus dem 'data' Ordner
font = createFont ("RockwellStd", 29);
textFont (font, 29);
textSize (32);

// Zeilenhöhe
int leading = 65;
// Laden der XML
xml = new XMLElement (this, "music_attributes.xml");
// Wieviele Knoten hat die XML?
int numNodes = xml.getChildCount ();

// Für jeden Knoten
for (int i = 0; i < numNodes; i++) {
  XMLElement kid = xml.getChild (i);
  // Auslesen von Knoten und Attribut
  String artist = kid.getContent ();
  String genre = kid.getStringAttribute ("genre");
  // Anzeigen im Sketch
  String display = artist + " (" + genre + ")";
  text (display, 20, leading * (i+1));
}
<?xml version="1.0" encoding="utf-8"?>
<artists>
    <artist genre="pop">Junior Boys</artist>
    <artist genre="grunge">Nirvana</artist>
    <artist genre="metal">Iron Maiden</artist>
</artists>
Bsp.: Text und Bild
In diesem Sketch wird neben dem Abbilden des Bandnamen und der Anzahl der gespielten Titel ebenfalls ein entsprechendes Bild geladen und im Sketchfenster platziert.
XMLElement xml;
PFont font;
PImage[] img;

void setup() {
  size (320, 240);
  background (0);
  smooth ();

  float lineHeight = 76;
  float yPos = 8;

  // Laden der Schrift. Diese muss zu diesem Zeitpunkt
  // im System aktiviert sein.
  font = createFont ("RockwellStd", 21);
  textFont (font, 18);

  // Laden des XML Dokuments
  xml = new XMLElement (this, "music_children.xml");

  // Leeres image array für die zu ladenen Bilder
  img = new PImage[xml.getChildCount ()];

  // Filtern der für den Sketch wichtigen Daten
  // aus dem geladnen XML Dokument.
  XMLElement[] artists = xml.getChildren("artist/name");
  XMLElement[] playCnt = xml.getChildren("artist/playcount");
  XMLElement[] images = xml.getChildren("artist/image");

  // Für jeden Datensatz im XML Dokument
  for (int i=0; i < artists.length; i++) {
    String name = artists[i].getContent ();
    String playCount = playCnt[i].getContent ();
    String imgURL = images[i].getContent ();

    // Darstellen von Bild und Schrift im Sketchfenster
    img[i] = loadImage (imgURL);
    float imgW = img[i].width / 1.5;
    float imgH = img[i].height / 1.5;
    
    String txt = name + "\n(" + playCount + " played)";
       
    image (img[i], 20, yPos, imgW, imgH);
    text (txt, 140, yPos + 21);
    
    // Verschieben der y Position
    yPos += lineHeight;
  }
}
<?xml version="1.0" encoding="utf-8"?>
<artists>
	<artist>
		<name genre="pop">Junior Boys</name>
		<playcount>7786</playcount>
		<image>http://userserve-ak.last.fm/serve/126/296727.jpg</image>
	</artist>
	<artist>
		<name genre="grunge">Nirvana</name>
		<playcount>386</playcount>
		<image>http://userserve-ak.last.fm/serve/126/296721.jpg</image>
	</artist>
	<artist>
		<name genre="metal">Iron Maiden</name>
		<playcount>1345</playcount>
		<image>http://userserve-ak.last.fm/serve/126/29672.jpg</image>
	</artist>      
</artists>