Elemente oder Attribute umbenennen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie müssen den Namen oder den Namensraum von Elementen oder Attributen in einem XML-Dokument ändern.

Lösung

Falls Sie nur eine kleine Anzahl von Attributen oder Elementen umbenennen müssen, dann verwenden Sie eine einfache Version des überschreibenden Kopiervorgangs, wie im folgenden Beispiel gezeigt.

Beispiel: Umbenennen von person in individual.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="copy.xslt"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8"/>
  <xsl:template match="person">
    <individual>
      <xsl:apply-templates select="@* | node()"/>
    </individual>
  </xsl:template>
</xsl:stylesheet>

Oder Sie verwenden alternativ xsl:element:

...
  <xsl:template match="person">
    <xsl:element name="individual">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
...

Das Umbenennen von Attributen ist genauso einfach:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="copy.xslt"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8"/>
  <xsl:template match="@lastname">
    <xsl:attribute name="surname">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>
</xsl:stylesheet>

Manchmal jedoch müssen Sie nicht den Namen, sondern den Namensraum ändern, wie im folgenden Beispiel zu sehen ist.

Beispiel: Ein Dokument mit dem Namensraum foo.

<foo:someElement xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo">
  <foo:aChild> 
    <foo:aGrandChild/>
    <foo:aGrandChild/>
  </foo:aChild>
</foo:someElement>

Für jedes Element im Namensraum foo erzeugen Sie nun ein neues Element im Namensraum bar, wie die beiden folgenden Beispiele zeigen.

Beispiel: Ein Stylesheet, das foo auf bar abbildet.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo" xmlns:bar="http://www.ora.com/XMLCookbook/namespaces/bar">
  <xsl:import href="copy.xslt"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="foo:*">
    <xsl:element name="bar:{local-name()}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Beispiel: Ausgabe.

<bar:someElement xmlns:bar="http://www.ora.com/XMLCookbook/namespaces/bar">
  <bar:aChild>
    <bar:aGrandChild/>
    <bar:aGrandChild/>
  </bar:aChild>
</bar:someElement>

Diskussion

Das Benennen ist eine wichtige Fertigkeit, die nur wenige Software-Fachleute beherrschen. (Anmerkung: Ein Beweis für meine Unfähigkeit, Namen zu vergeben, ist die Tatsache, dass mein Sohn zwei volle Tage ohne Namen auf der Welt verbringen musste. Meine Frau und ich fanden einfach keinen guten Namen, der uns beiden gefiel. Natürlich verstanden wir beide die Wichtigkeit der Wahl eines guten Namens, und wir glauben, Leonardo stimmt uns zu.) Sie sollten daher wissen, wie Sie Dinge umbenennen, wenn Sie beim ersten Mal nicht den richtigen Namen gefunden haben.

Wenn viele Elemente oder Attribute umbenannt werden müssen, dann könnten Sie einen generischen, tabellenorientierten Ansatz verfolgen, wie er in den drei folgenden Beispielen gezeigt wird.

Beispiel: Ein generisches, tabellenorientiertes Stylesheet zum Umbenennen.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ren="http://www.ora.com/namespaces/rename">
  <xsl:import href="copy.xslt"/>
  <!--Im importierenden Stylesheet überschreiben -->
  <xsl:variable name="lookup"  select="/.."/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match="*">
    <xsl:choose>
      <xsl:when test="$lookup/ren:element[@from=name(current())]">
        <xsl:element name="{$lookup/ren:element[@from=local-name(current())]/@to}">
          <xsl:apply-templates select="@*"/>
          <xsl:apply-templates/>
        </xsl:element>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-imports/>
     </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template match="@*">
    <xsl:choose>
      <xsl:when test="$lookup/ren:attribute[@from=name(current())]">
        <xsl:attribute name="{$lookup/ren:attribute[@from=name(current())]/@to}">
          <xsl:value-of select="."/>
        </xsl:attribute>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-imports/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Beispiel: Verwendung des tabellenorientierten Stylesheets.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ren="http://www.ora.com/namespaces/rename">
  <xsl:import href="TableDrivenRename.xslt"/>
  <!-- Die Suchtabelle laden. Wir definieren sie lokal, sie kann aber auch aus einer externen Datei kommen -->
  <xsl:variable name="lookup"  select="document('')/*[ren:*]"/>
  <!-- Definieren der Umbenennungsregeln -->
  <ren:element from="person" to="individual"/>
  <ren:attribute from="firstname" to="givenname"/>
  <ren:attribute from="lastname" to="surname"/>
  <ren:attribute from="age" to="yearsOld"/>
</xsl:stylesheet>

Beispiel: Output.

<?xml version="1.0" encoding="UTF-8"?>
<people which="MeAndMyFriends">
  <individual givenname="Sal" surname="Mangano" yearsOld="38" height="5.75"/>
  <individual givenname="Mike" surname="Palmieri" yearsOld="28" height="5.10"/>
  <individual givenname="Vito" surname="Palmieri" yearsOld="38" height="6.0"/>
  <individual givenname="Vinny" surname="Mari" yearsOld="37" height="5.8"/>
</people>

Sie können diesen Ansatz auch verwenden, wenn einige Elemente oder Attribute unter Beachtung des Kontextes verarbeitet werden müssen. Schauen Sie sich beispielsweise das folgende Dokumentfragment an:

<clubs>
  <club name="The 500 Club">
    <members>
      <member name="Joe Smith">
        <position name="president"/>
      </member>
      <member name="Jill McFonald">
        <position name="treasurer"/>
      </member>
      <!-- ... -->
    <members>
  </club>
  <!-- ... -->
<clubs>

Nehmen Sie einmal an, Sie wollen das Attribut @name in @title ändern, allerdings nur für die position-Elemente. Wenn Sie den tabellenorientierten Ansatz verwenden, werden alle Elemente, die ein name-Attribut enthalten, geändert. Die Lösung besteht darin, ein Template zu erzeugen, das das vorgegebene Verhalten für alle Elemente mit Ausnahme von position außer Kraft setzt:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ren="http://www.ora.com/namespaces/rename">
  <xsl:import href="TableDrivenRename.xslt"/>
  <!-- Laden der Suchtabelle. Wir definieren sie lokal, sie kann aber auch aus einer externen Datei kommen -->
  <xsl:variable name="lookup"  select="document('')/*[ren:*]"/>
  <!-- Definieren der Umbenennungsregeln -->
  <ren:attribute from="name" to="title"/>
  <!-- ÜBERSCHREIBEN: Einfach alle Namen kopieren, die keine Attribute des position-Elements sind -->
  <xsl:template match="@name[not(parent::position)]">
    <xsl:copy/>
  </xsl:template>
</xsl:stylesheet>

Beim Umbenennen des Namensraums durch Kopieren könnte sich der alte Namensraum hartnäckig weigern zu verschwinden, selbst wenn er nicht benötigt wird. Betrachten Sie noch einmal das foo-Dokument, dieses Mal mit einem zusätzlichen Element aus einem Namensraum doc:

<foo:someElement xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo" xmlns:doc="http://www.ora.com/XMLCookbook/namespaces/doc">
  <foo:aChild>
    <foo:aGrandChild/>
    <foo:aGrandChild>
      <doc:doc>Diese Dokumentation darf auf keine Weise entfernt oder verändert werden.</doc:doc>
    </foo:aGrandChild>
  </foo:aChild>
</foo:someElement>

Falls Sie das Stylesheet zum Umbenennen des Namensraums auf dieses Dokument anwenden, wird der Namensraum foo zusammen mit dem Element doc mitgeführt:

<bar:someElement xmlns:bar="http://www.ora.com/XMLCookbook/namespaces/bar">
  <bar:aChild>
    <bar:aGrandChild/>
    <bar:aGrandChild>
      <doc:doc xmlns:doc="http://www.ora.com/XMLCookbook/namespaces/doc" xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo">Diese Dokumentation darf auf keine Weise entfernt oder verändert werden.</doc:doc>
    </bar:aGrandChild>
  </bar:aChild>
</bar:someElement>

Dies liegt daran, dass das doc-Element von xsl:copy verarbeitet wird. Sowohl xsl:copy als auch xsl:copy-of kopieren immer alle Namensräume, die mit einem Element verbunden sind. In XSLT 2.0 besitzen xsl:copy und xsl:copy-of jeweils ein optionales Attribut namens copy-namespaces, das Sie auf yes oder no setzen können. Da das doc-Element in Elemente aus dem Namensraum foo eingeschlossen ist, besitzt es einen foo-Namensraumknoten, auch wenn dies in der Ausgabe nicht direkt sichtbar ist. Um das Kopieren dieses unerwünschten Namensraums zu vermeiden, verwenden Sie xsl:element, womit Sie sicherstellen, dass die Elemente neu angelegt und nicht kopiert werden:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo" xmlns:bar="http://www.ora.com/XMLCookbook/namespaces/bar">
  <xsl:import href="copy.xslt"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <!-- Erzeugt für alle Elemente ein neues Element mit demselben Namen und Namensraum -->
  <xsl:template match="*">
    <xsl:element name="{name( )}" namespace="{namespace-uri( )}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="foo:*">
    <xsl:element name="bar:{local-name( )}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Sie können diese Technik sogar dazu einsetzen, alle Namensräume aus einem Dokument zu entfernen:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="copy.xslt"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="*">
    <xsl:element name="{local-name( )}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>
Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de