XML-Tutorial VI: XLinks & XPointer

Gelesen bei AboutWebDesign.de
URL: http://www.aboutwebdesign.de/awd/content/1028646030.shtml

Was wäre HTML ohne Links? Bestenfalls unbrauchbar.

Aber nicht nur HTML benötigt Links. Es ist dagegen sehr wahrscheinlich, dass Sie beim Definieren Ihrer eigenen Elemente irgendwann auf den Fall stoßen, dass Sie zwei verschiedene Stellen im Dokument bzw. in verschiedenen Dokumenten miteinander in Verbindung bringen müssen. Und schon sind Sie in der Situation, einen Link zu benötigen.

Im Folgenden beschäftigen wir uns erst einmal mit der Problematik, bestimmte Stellen in einem Dokument anzugeben - eine Voraussetzung, um überhaupt einen Link erstellen zu können, denn der braucht ja ein Ziel. Danach geht es um XLinks, den Standard zur Link-Erstellung.

Positionsangaben


Das Dokument finden


Um etwas linken zu können, muss man wissen, wo es steht. Das hört sich zunächst sehr einfach an, denn schließlich kennen wir ja meist den URI (Uniform Resource Identifier) des Dokuments, also so etwas wie http://www.aboutwebdesign.de/index.html.

Etwas anders funktioniert der FPI, der "Formal Public Identifier". Das sind Konstrukte wie -//W3C//DTD XHTML 1.0 Strict//EN, die Sie schon aus anderen Tutorial-Teilen kennen. Worum geht es dabei? Das Minus-Zeichen am Anfang bedeutet, dass die Organisation, die das gewünschte Dokument herausbringt, nicht öffentlich (z.B. bei der ISO) registriert ist. Danach folgt ein Kürzel der Organisation: hier ist es das W3C. Nach einem erneuten Doppelslash schließt sich die Beschreibung dessen an, was man haben möchte, nämlich einen XHTML-DTD. Zum Schluss steht die Sprachangabe, hier Englisch.

Der Hintergedanke: so lassen sich Dokumente spezifizieren, ohne den genauen URI zu kennen. Das Dokument selbst kann beliebig oft den Server wechseln, die Links darauf bleiben trotzdem gültig. Leider braucht man für FPIs jedoch einen Verzeichnis-Server, der den FPI in einen URI umwandelt, und da hapert es im Moment noch. Daher wird hinter einem FPI auch meist ein URI, also eine physische Positionsangabe, gemacht. Im Folgenden gehen wir davon aus, dass vorerst nur URIs verwendet werden.

Einzelne Stellen im Dokument finden


Das Dokument selbst wird einfach über altbekannte URIs angesprochen. Doch was, wenn wir eine bestimmte Stelle im Dokument ansprechen wollen?

Aus HTML kennen Sie so einen ähnlichen Mechanismus: mit <a name="xyz">...</a> markieren Sie eine Stelle, und mit <a href="#xyz">...</a> linken Sie darauf.

In XML sind die Dinge ähnlich gelagert, nur etwas komplizierter. Es gibt einen Mechanismus, der es einem erlaubt, auf bestimmte Stellen im Dokument sehr flexibel zuzugreifen. Sein Name ist "XPointer". Verwendet wird er so:

URI#XPointer

Wird ein solcher Link aufgerufen, dann wird erst der URI aufgerufen und dann der XPointer verarbeitet.

XPointer-Syntax


ID-Verweise


Der einfachste XPointer zeigt einfach auf ein durch einen ID-Wert spezifiziertes Element. Wenn Sie sich an das vorige Kapitel erinnern: dort war es möglich, einem Attribut den ID-Status zu geben. Als XPointer wird in solch einem Fall einfach der Wert des ID-Attributs verwendet.

Beispiel:

XML-Code:
...
<mitarbeiter id="abc">
...


Um genau auf diesen einen Mitarbeiter zu zeigen, verwenden Sie nun einen solchen Link:

http://url/des/dokuments.xml#abc

Bitte beachten Sie, dass hier nicht ausschlaggebend ist, dass das Attribut id heißt. Es kommt vielmehr darauf an, dass der Attribut-Typ in der DTD auf ID gesetzt wurde.

Affen an Bäumen


Um folgende Inhalte zu verstehen, ist die Baum-Metapher enorm wichtig: stellen Sie sich vor, aus Ihrem XML würde ein hierarchisches Baumdiagramm erzeugt. Ein Beispiel dafür finden Sie hier oder auf nebenstehender Grafik.

ID-Verweise wie oben sind, gemessen an den Möglichkeiten der XPointer, nur simple Aufwärm-Übungen. XPointer klettern am XML-Baum. Wie das geht, erfahren Sie jetzt.

Unser Beispiel-Code


Zur Erklärung verwenden wir im Folgenden diesen Code:

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE mb-datenbank SYSTEM "test.dtd">
<mb-datenbank>

<abteilung>
    <name>
        IT-Abteilung
    </name>
    <angehoerige>
        <mitarbeiter>
            <nachname>Meier</nachname>
            <telefon>3421</telefon>
        </mitarbeiter>
        <mitarbeiter>
            <nachname>Müller</nachname>
            <telefon>5564</telefon>
        </mitarbeiter>        
        <mitarbeiter>
            <nachname>Hinkelstein</nachname>
            <telefon>3456</telefon>
            <untergebene>
                <mitarbeiter>
                    <nachname>Schulz</nachname>
                    <telefon>3321</telefon>
                </mitarbeiter>        
                <mitarbeiter id="zupul">
                    <nachname>Zupul</nachname>
                    <telefon>6768</telefon>
                    <untergebene>
                        <mitarbeiter herkunft="frankreich">
                            <nachname>Arletz</nachname>
                            <telefon>7593</telefon>
                        </mitarbeiter>                    
                    </untergebene>
                </mitarbeiter>                        
            </untergebene>
        </mitarbeiter>        
    </angehoerige>
</abteilung>

</mb-datenbank>


Absoluter Ansatzpunkt


Zunächst müssen Sie dem XPointer einen Ansatzpunkt geben. Sozusagen setzen Sie den Affen erst auf einen Ast und erklären ihm dann, wie es von dort aus weiter geht. Es gibt vier verschiedene Ansatzpunkte. Alle sind absolut, d.h. in ihrer Position eindeutig. Daran schließen sich meist noch weitere, relative Anweisungen an, deren Endergebnis von der Position des absoluten Anfangspunkts abhängt.

root() spezifiziert einen imaginären Knoten, dessen Kind der Wurzel-Knoten des Dokuments ist. root().child(1) würde also auf <mb-datenbank> zeigen.

id(abc) zeigt zum Element mit dem als ID deklarierten Attribut-Wert abc. id(zupul).child(1) zeigt daher auf das nachname-Tag, das zum Mitarbeiter Zupul gehört.

origin() zeigt zum Element, das den Link ausgelöst hat. Daher macht es keinen Sinn, origin zusammen mit einem absoluten URI am Anfang zu verwenden.

html(abc) zeigt zum ersten A-Element, dessen name-Attribut den Wert abc hat.

Relativ geht es weiter


Der Affe hat zugepackt. Flüstern Sie ihm nun ins Ohr, wie es weitergehen soll - mit relativen Ausdrücken, also so etwas in der Art wie "Gehe von hier 30 Schritte nach rechts".

Relative Ausdrücke werden mit einem Punkt an den absoluten Startausdruck angeschlossen. Wenn Sie wollen, können Sie auch mehrere relative Ausdrücke verbinden, ebenfalls mit einem Punkt.

XPointer unterstützt diese relativen Lokalisierungsausdrücke, die übrigens meist einen ganzen Satz von Ergebnissen zurückliefern. Wie man darauf einen auswählt, dazu weiter unten mehr.

child() findet direkte Nachfahren des aktuellen Knotens (keine Enkel!).

ancestor() findet Vorgänger-Knoten, aber nur direkte Vorfahren.

following() findet Knoten, die nach dem aktuellen Knoten enden. Hier wird zuerst in der Tiefe gesucht (siehe unten).

preceding() findet Knoten, die vor dem aktuellen beginnen.

fsibling() und psibling() finden Geschwister des aktuellen Knotens, erstere nur solche, die hinter dem aktuellen Knoten kommmen ("jüngere Geschwister"), letztere nur solche, die vor dem aktuellen Knoten kommen ("ältere Geschwister").

descendant() findet Nachfahren (auch Enkel etc.) des aktuellen Knotens. Dabei wird nach einer zuerst-in-die-Tiefe-Reihenfolge gesucht. Das heißt, dass der Algorithmus immer zuerst versucht, noch eine Ebene weiter nach links abzusteigen, bevor er sich einem rechten Kindknoten zuwendet. Wenn er das nicht mehr kann, steigt er wieder nach oben, bis er den ersten Kind-Knoten erreicht, wovon er wieder den am weitesten links stehenden auswählt - das Spiel beginnt von vorne.

Relativ beschränkt


Die Klammern hinter den Funktionen sind nicht nur da, damit es schöner aussieht. Sie akzeptieren bis zu vier Argumente, die alle durch ein Komma getrennt werden. Der Sinn der Argumente ist, den Ergebnis-Satz auf einen Knoten einzuengen. Übrigens können Sie ein Argument nur verwenden, wenn alle vorherigen auch gesetzt wurden. Nur das dritte Argument zu verwenden wäre vielleicht praktisch, ist aber unmöglich: das erste und das zweite müssen auch gesetzt sein.

Das erste Argument ist die Knoten-Nummer - sie wählt aus dem Ergebnis-Satz einfach den betreffenden Knoten aus. Deshalb wählt root().child(1).child(1).child(2).child(2) den Mitarbeiter Müller aus. Ein all anstelle einer Zahl selektiert einfach alle Knoten. Die Knoten-Nummer wird, sofern die anderen Argumente gesetzt sind, als letzte ausgewertet.

Das zweite Argument ist der Knoten-Typ, der zunächst einmal dem Namen des Elements entsprechen muss. root().decendant(1, name) findet also das Abteilungsnamen-Element. Es gibt noch einige andere Werte, die hier gesetzt werden können: #text findet Zeichendaten, pi findet Steueranweisungen, #comment findet Kommentare, element bzw. * findet Elemente und all findet alles.

Die letzten beiden Argumente beziehen sich auf Element-Attribute. Das dritte ist der Attribut-Name, das vierte der Attribut-Wert. Als Wildcard werden in beiden Fällen Sternchen akzeptiert. root().(1,Mitarbeiter,herkunft,polen) würde also alle Mitarbeiter finden, deren herkunft-Attribut auf den Wert polen gesetzt wurde. Schade, die gibt es nicht. root().(1,Mitarbeiter,herkunft,*) findet dagegen Herrn Arletz, denn bei ihm wurde das Attribut ja gesetzt.

Zwischen-Fazit


Das Angeben einer Position ist also gar nicht so schwierig: zuerst meist eine URL, dann die Raute, dann ein XPath. Die bestehen aus einem absoluten Suchausdruck und eventuell dahinter folgenden relativen Angaben. Lassen Sie sich von denen nicht entmutigen - damit hat jeder am Anfang seine Probleme. Ein wenig Herumexperimentieren hilft da fast immer.

XLinks


Sie haben jetzt die Technik, eine Position zu bestimmen. Daher können wir uns jetzt der Technik zuwenden, konkrete Links zu erstellen. Dazu verwenden wir die "XML Linking Language", kurz "XLink". In XML kann jedes Element zu einem Link gemacht werden. Sehen Sie sich folgendes Beispiel eines XLinks an:

<mitarbeiter
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xlink:type="simple"
    xlink:href="http://www.aboutwebdesign.de/irgendwas.xml"
    xlink:show="new"
    xlink:actuate="onRequest"
>Link zu AWD</mitarbeiter>


In Detail: Attribute


Die Attribute sind weitgehend selbsterklärend: zunächst einmal der neue Namensraum, dann der Typ. Hier ist es simple, auf andere Alternativen werden wir im Moment auch nicht eingehen. Das bedeutet, dass es zumindest ein href-Attribut geben muss, das die URL des Links enthält (event. mit XPointer hinten angeschlossen).

show gibt an, wie der Link geöffnet wird. new steht wohl meistens für neues Fenster, embed sollte für eine Plugin-ähnliche Darstellung sorgen und replace dafür, dass die aktuellen Inhalte durch die des Link-Ziels ersetzt werden. Das kann aber von Parser zu Parser variieren.

actuate beschreibt, wann der Link verfolgt werden soll. onRequest heißt "auf Aktion des Benutzers", also z.B. durch einen Klick. onLoad steht für direktes Laden.

Deskription


Der beschreibende Text kann entweder innerhalb des Elements stehen, so wie im Beispiel, oder er kann als xlink:title-Attribut angegeben werden.

Zusätzlich gibt es noch die Möglichkeit, ein xlink:role-Attribut anzugeben, das einen URI enthalten muss. Der sollte auf irgendetwas verweisen, was die Rolle oder Bedeutung des Link-Ziels näher beschreibt. So könnte man z.B. http://www.aboutwebdesign.de/Popmusik angeben für moderne Musik, http://www.aboutwebdesign.de/Klassik dagegen für klassische Musik.

DTDs


Namensräume machen Probleme in Verbindung mit DTDs. Daher werden Sie Ihre DTD, wenn Sie XLinks verwenden, dahingehend anpassen müssen, dass Sie in der DTD die XLink-Attribute deklarieren.

Fazit


Link-Erstellung gehört jetzt zum Repertoire Ihrer Fähigkeiten. Machen Sie häufig Gebrauch davon, denn es ist die Vernetzung, die Information erst sinnvoll macht.