Alex Schneider: Variablen-Neuzuweisung

Beitrag lesen

Hallo Alex,

ich habe folgendes Problem:
anscheinend ist es nicht möglich einer Variablen einen neuen Stringwert zuzuweisen.

Richtig. Variablen kann man in XSLT nicht updaten. XSLT ist eine sogenannte funktionale Programmiersprache, d.h. eine Sprache ohne Seiteneffekte. Dies hat Vor- und Nachteile. Ein Nachteil ist, dass man es als Programmierer, der von herkömmlichen Sprachen kommt zunächst schwer begreift, hat man es aber einmal begriffen sind einige Dinge mit XSLT wesentlich leichter zu erledigen. Interessierst Du dich näher dafür, kann ich Dir nur zur Lektüre von Michael Kays Standardwerk zu XSLT raten. Dort wird das ausführlich diskutiert:
http://www.amazon.de/exec/obidos/ASIN/1861005067/qid=1017076802/sr=1-1/ref=sr_1_1_1/028-8589672-6002153

Um genau zu sein, habe ich dieses Problem:

ich möchte diese Struktur:
<GRUPPE>
<PERSON>
  <NAME>Meyer</NAME>
  <VORNAME>Hans</VORNAME>
</PERSON>
<PERSON>
  <NAME>Meyer</NAME>
  <VORNAME>Heidi</VORNAME>
</PERSON>
<PERSON>
  <NAME>Schulz</NAME>
  <VORNAME>Daniela</VORNAME>
</PERSON>
<PERSON>
  <NAME>Schulz</NAME>
  <VORNAME>Herbert</VORNAME>
</PERSON>
</GRUPPE>

in diese wandeln:
<BSP>
<FAMILIE>
  <NAME>Meyer</NAME>
  <MITGLIED>
   <VORNAME>Hans</VORNAME>
   <VORNAME>Heidi</VORNAME>
  </MITGLIED>
</FAMILIE>
<FAMILIE>
  <NAME>Schulz</NAME>
  <MITGLIED>
   <VORNAME>Daniela</VORNAME>
   <VORNAME>Herbert</VORNAME>
  </MITGLIED>
</FAMILIE>
</BSP>

Ich dachte mir nun, ich könnte mir "NAME" abspeichern und dann alle mit diesen Namen herausgreifen (for-each). Bei jedem Durchgang durch das Skript wollte ich überprüfen, ob der alte NAME dem neuen entspricht oder nicht. Das ging aber nicht, weil eine Neuzuweisung wohl nicht möglich ist.

Folgendes Stylesheet löst Dein Problem, wenn es vermutlich bei größeren Dateien etwas langsam sein wird.

<?xml version="1.0"?>

<xsl:stylesheet
     version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
    <BSP>
      xsl:apply-templates/
    </BSP>
  </xsl:template>

<xsl:template match="PERSON">
    <xsl:if test="NAME[not(.=preceding::NAME)]">
    <FAMILIE>
      <NAME>
         <xsl:value-of select="NAME"/>
       </NAME>
       <MITGLIED>
         <xsl:call-template name="mitglieder">
           <xsl:with-param name="name">
             <xsl:value-of select="NAME"/>
           </xsl:with-param>
         </xsl:call-template>
       </MITGLIED>
    </FAMILIE>
    </xsl:if>
  </xsl:template>

<xsl:template name="mitglieder">
    <xsl:param name="name"/>
    <xsl:for-each select="//VORNAME[preceding-sibling::NAME=$name]">
      <VORNAME>
        <xsl:value-of select="."/>
      </VORNAME>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Zunächst prüftst du im Template für die <PERSON>-Elemente, ob der <NAME> einer Person nicht bereits einmal vorher getaucht ist:

<xsl:if test="NAME[not(.=preceding::NAME)]">

Der Test ist etwas aufwendig von der Performance her. Es geht auch anders. Wie ist allerdings nicht ganz einfach, steht aber hier beschrieben:
http://www.dpawson.co.uk/xsl/sect2/N2696.html#d118e129

Dann rufst Du ein Template auf, das als Parameter den Wert des derzeitig vom Prozessor bearbeiteten <NAME>-Elements erhält und dann selektierst du mit xsl:for-each alle <VORNAMEN>, deren zugehöriger <NACHNAME> gleich dem übergebenen Parameter ist.

Bei XSLT musst du mehr regelbasiert denken und immer den Transformationsprozess vor Augen haben. Variablen hochzählen funktioniert nicht.

Gruß
Franz

Hallo Franz,

eigentlich müsste man Dich für Deine Tipps bezahlen ...

vielen Dank
Alex