Armin Heimbach: xPath: Verständnisproblem bei Adressierung mit Positionsangabe

Hallo,
ich habe ein Verständnisproblem bei der Adressierung von Knoten.

Gegeben ist folgende Besipieldatei:

<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet type="text/xsl" href="mitarbeiter1.xsl" ?>

<mb-datenbank>
        <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>
  </untergebene>
         </mitarbeiter>
</mb-datenbank>

Ausgeben möchte ich die Datei mit folgendem Stylesheet

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:template match="/">
    <html>
        <body>
                  <p><b>Anzahl gesammt:<xsl:value-of select="count (.//nachname)"/></b></p>
                  <p><xsl:value-of select =".//nachname [1]" /></p>
    <p><xsl:value-of select =".//nachname [2]" /></p>
    <p><xsl:value-of select =".//nachname [3]" /></p>
    <p><xsl:value-of select =".//nachname [4]" /></p>
        </body>
    </html>
</xsl:template>
</xsl:stylesheet>

Der Locationpath ".//nachname" müsste meiner Meinung nach ein Knotenset mit vier Knoten zurückgeben.
Die count Funktion gibt auch die Zahl vier zurück.
Wenn ich aber die Knoten mit einem Prädikat adressieren möchte bekomme ich nur bei "[1]" ein Ergebniss.

Das Beispiel habe ich mit dem IE6 (msxml 4.0) und Saxon getestet und bei beiden die gleichen Ergebnisse erhalten.

Vielleicht kann mir jemand sagen wo mein Denkfehler liegt.

armin

  1. Hallo,

    ich habe ein Verständnisproblem bei der Adressierung von Knoten.
    Der Locationpath ".//nachname" müsste meiner Meinung nach ein Knotenset mit vier Knoten zurückgeben.

    Nein. 1) der Punkt ist (hier) unnötig. 2) du bekommst genau den ersten gefundenen <nachnamen> zurück.

    Die count Funktion gibt auch die Zahl vier zurück.

    Ja, weil count() eben zählt. count() zählt dir hier alle gefundenen <nachnamen> zusammen.

    Wenn ich aber die Knoten mit einem Prädikat adressieren möchte bekomme ich nur bei "[1]" ein Ergebniss.

    Ja, ist logisch: du bekommst genau den ersten gefundenen <nachnamen> zurück.

    Und weil du jedes mal von vorne beginnst gibt es keinen 2. <nachname>; teste mal: <xsl:value-of select ="boolean(//nachname[2])" /> du bekommst "false" zurück.

    Du musst <nachname> in einem echten Knotenset bekommen:
    <xsl:variable name="namen" select="//nachname" />
    <p><b>Anzahl gesammt:<xsl:value-of select="count($namen)"/></b></p>
    <p><xsl:value-of select ="$namen[1]" /></p>
    <p><xsl:value-of select ="$namen[2]" /></p>
    <p><xsl:value-of select ="$namen[3]" /></p>
    <p><xsl:value-of select ="$namen[4]" /></p>

    Grüße
    Thomas

  2. allo Armin,

    Der Locationpath ".//nachname" müsste meiner Meinung nach ein Knotenset mit vier Knoten zurückgeben.

    Tut er auch. Nur wird dieser Ausdruck als Wert des select-Attributs des xsl:value-of-Elements als String ausgewertet. Falls das Ergebnis des Ausdrucks kein String ist (sondern wie in Deinem Falle eine Knotenmenge) erfolgt eine implizite Konvertierung wobei für eine Knotenmenge nur der erste Knoten in Dokumentenordnung ausgewählt wird.

    Die count Funktion gibt auch die Zahl vier zurück.
    Wenn ich aber die Knoten mit einem Prädikat adressieren möchte bekomme ich nur bei "[1]" ein Ergebniss.

    Offensichtlich erfolgt die Umwandlung *vor* der Einschränkung durch ein Prädikat, was ich auch nicht ganz einleuchtend finde, aber es ist so. Möglich wäre ja auch die Auswahl aller <nachname>-Knoten, dann das Filtern des ersten bzw. zweiten Knoten und dann die Umwandlung in einen String

    Eine Lösung ohne Variablen wäre auch:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >

    <xsl:template match="/">
        <html>
          <head>
            <title>Test</title>
          </head>
          <body>
            <p><b>Anzahl gesammt:<xsl:value-of select="count (.//nachname)"/></b></p>
            <xsl:apply-templates select="//nachname"/>
          </body>
        </html>
      </xsl:template>
      <xsl:template match="//nachname">
        <p><xsl:value-of select ="." /></p>
      </xsl:template>
      </xsl:stylesheet>

    Gruß
    Franz

    1. Hallo Fraqnz,

      Eine Lösung ohne Variablen wäre auch:

      ich weiss ich bin schlimm, aber wenn er nur alle namen haben wollte ginge es ja noch einfacher:

      <xsl:template match="/">
          <html>
            <head>
              <title>Test</title>
            </head>
            <body>
              <p><b>Anzahl gesammt:<xsl:value-of select="count (.//nachname)"/></b></p>
              <xsl:for-each select="//nachname">
               <p><xsl:value-of select ="." /></p>
              </xsl:for-each>
            </body>
          </html>
        </xsl:template>

      aber wenn er nur den 2. und 4. etc namen haben will, geht so nicht mehr ;-)

      Grüße
      Thomas

      1. Hallo Thomas,

        ich weiss ich bin schlimm,

        ne gar nicht *hmpf*  ;-)

        <xsl:template match="/">
            <html>
              <head>
                <title>Test</title>
              </head>
              <body>
                <p><b>Anzahl gesammt:<xsl:value-of select="count (.//nachname)"/></b></p>
                <xsl:for-each select="//nachname">
                 <p><xsl:value-of select ="." /></p>
                </xsl:for-each>
              </body>
            </html>
          </xsl:template>

        naja, ist halt Pull statt Push, und höchstens in LOC einfacher, Ich bevorzuge Pull, da es flexibler ist und besser geeignet das regelbasierte Prinzip von XSLT zu verdeutlichen. Letzlich ist es aber "Geschmackssache" und natürlich gibt es auch Fälle, in denen es ohne for-each nicht geht.

        aber wenn er nur den 2. und 4. etc namen haben will, geht so nicht mehr ;-)

        Ja, aber wer sagt denn das er das will ;-)

        Gruß
        Franz

        1. Ich bevorzuge Pull

          Quatsch mit Soße, *Push* natürlich

          Gruß
          Franz