Alex Schneider: Variablen-Neuzuweisung

Hallo,

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

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.

Gruß
Alex

  1. 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

    1. 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

      1. Hallo Alex,

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

        ja gute Idee, weisste nen netten Arbeitgeber oder soll ich Dir gleich meine Kontonummer schicken *fg*

        Gruß
        Franz