Thomas Mell: Frage zu xpath

Hallo,
folgender XML-Node:
<node att="a b c" />
Ist es möglich mit xpath diesen Node über die Angabe eines Teils des Attributwertes zu finden ? Also z.B. "Gib mir alle Elemente die im Attribut 'att' den Wert 'b' enthalten, quasi wie es bei CSS mit ~= möglich ist.

Grüße
Thomas

  1. Hallo Thomas,

    Ist es möglich mit xpath diesen Node über die Angabe eines Teils des Attributwertes zu finden?

    Ja, mit der XPath-Funktion contains().

    Also z.B. "Gib mir alle Elemente die im Attribut 'att' den Wert 'b' enthalten, quasi wie es bei CSS mit ~= möglich ist.

    //*[contains(@att, "b")]

    Erklärt:

    //* selektiert alle Elementknoten.
    //*[foo] selektiert alle Elementknoten bei denen die der Ausdruck „foo“ True zurückgibt. Nennt man auch Prädikat
    contains(@att, "b") ist so eine Bedingung. Die Funktion liefert True zurück, wenn der String "b" im Wert des Attributes "att" enthalten ist, ansonsten false.

    Dieser Ausdruck liefert ausgeführt gegenüber der Forumshauptdatei z.B. alle li-Elemente zurück, deren class-Attribut den String "visited" enthält ...

    //li[contains(@att, "visited")]

    ... selektiert also alle gelesenen Threads. Praktisch, da so ein class-Attribut in HTML ja auch andere Klassen enthalten kann ("no-archive" und so), es in XPath 1.0 meines Wissens aber keine Möglichkeiten gibt, auf der Ebene der Klassen zu arbeiten.

    Mit XPath 2.0 kann man da wohl mehr machen, vermute ich, da man dort auch mit XML Schema Datentypen operieren kann; ein token-basiertes Attribut hätte dann den Datentyp xsd:NMTOKENS. Allerdings kenne ich mich im Lande von XPath 2.0 und XQuery 1.0 so gut wie kaum aus und habe auch die Vermutung, das wäre komplizierter, als ein einfache Substring-Überprüfung wie oben. Aber vielleicht kann Thomas J.S. da mehr sagen. ;)

    Tim

    1. Hallo Gunnar,

      //li[contains(@att, "visited")]

      Tztztz: //li[contains(@class, "visited")]

      Tim

    2. Hello out there!

      contains(@att, "b") ist so eine Bedingung. Die Funktion liefert True zurück, wenn der String "b" im Wert des Attributes "att" enthalten ist

      Aber auch bei "xxb". Das könnte ungewollt sein.

      See ya up the road,
      Gunnar (der ohne [tm]) ;-)

      --
      „Wer Gründe anhört, kommt in Gefahr nachzugeben.“ (Goethe)
    3. Hi Tim
      genau dafür brauche ich es - alle Elemente mit einer Klasse x finden.
      Wie Du schon sagst, es können mehrere Klassen vorkommen und contains findet ja auch Klassen in denen die gesuchte Zeichenfolge "reinpasst".
      Dafür habe ich aber ne Lösung gefunden - nicht schön aber es funktioniert:
      //*[
      @class = 'b'
      or contains(@class, ' b ')
      or starts-with(@class, 'b ')
      or substring(@class, string-length(@class) - string-length('b')) = ' b'
      ]

      Finden in den folgenden Beispielen richtigerweise die ersten 4:
      <g class="b"/>
      <g class="b c"/>
      <g class="a b c"/>
      <g class="a b"/>
      <g class="aber"/>
      <g class="ab er"/>
      <g class="a ber"/>
      <g class="baer"/>
      <g class=" baer"/>
      <g class="bergab"/>
      <g class="bergab "/>

      In .NET gibt es sogar Ergebnisse wenn sich zwischen den Klassen andere Whitespaces befinden (tab, Zeilenumbrüche). Diese werden dann Hexadezimal (0d und 0a) oder unverändert (tab) ausgegeben.
      Ist das eine Eigenart von .NET oder ist das bei xpath so ?

      Vielen Dank und Grüße
      Thomas

      1. Hi Tim
        genau dafür brauche ich es - alle Elemente mit einer Klasse x finden.
        Wie Du schon sagst, es können mehrere Klassen vorkommen und contains findet ja auch Klassen in denen die gesuchte Zeichenfolge "reinpasst".
        Dafür habe ich aber ne Lösung gefunden - nicht schön aber es funktioniert:

        Falls die "Klassen" durch Leerzeichen getrennt sind:
        contains(@attribut, ' gesuchtezeichefloge ')

        Grüße
        Thomas

        1. Hello out there!

          Falls die "Klassen" durch Leerzeichen getrennt sind:
          contains(@attribut, ' gesuchtezeichefloge ')

          Hm, findest du damit <foo class="gesuchtezeichefloge nichtgesuchte dieauchnicht">?

          See ya up the road,
          Gunnar

          --
          „Wer Gründe anhört, kommt in Gefahr nachzugeben.“ (Goethe)
          1. Hallo,

            Falls die "Klassen" durch Leerzeichen getrennt sind:
            contains(@attribut, ' gesuchtezeichefloge ')

            Hm, findest du damit <foo class="gesuchtezeichefloge nichtgesuchte dieauchnicht">?

            Wieso stellst du eine Frage, auf die du die Antwort kennst?

            Grüße
            Thomas

            1. Tach,

              Wieso stellst du eine Frage, auf die du die Antwort kennst?

              wegen der Griechen vermutlich.

              mfg
              Woodfighter

      2. Hallo Thomas,

        Dafür habe ich aber ne Lösung gefunden - nicht schön aber es funktioniert:

        Das reicht ja auch. ;) Und es ist eine recht praktikable Lösung für das Problem.

        In .NET gibt es sogar Ergebnisse wenn sich zwischen den Klassen andere Whitespaces befinden (tab, Zeilenumbrüche). Diese werden dann Hexadezimal (0d und 0a) oder unverändert (tab) ausgegeben. Ist das eine Eigenart von .NET oder ist das bei xpath so ?

        Ich hab keine Ahnung von .NETs XPath-Implementierung, aber für mich scheint das so, als sei das ein Resultat der in XML eingebauten Attribut-Normalisierung.

        Tim

    4. Tach Tim,

      Mit XPath 2.0 kann man da wohl mehr machen, ...

      Ja, dazu mal ein Ansatz mit der neuen XPath-Funktion fn:matches():

      <?xml version="1.0" encoding="ISO-8859-1"?>  
      <xsl:stylesheet version="2.0"  
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
        xmlns:fn="http://www.w3.org/2005/xpath-functions">  
        
        <xsl:template match="root">  
        
          <xsl:variable name="className" select="'b'"/>  
          <xsl:value-of select="for $a in //@* return fn:matches($a,fn:concat('(^|\s)',$className,'(\s|$)'))"/>  
        
        </xsl:template>  
        
      </xsl:stylesheet>
      

      Bezogen auf diese XML-Instanz:

      <?xml version="1.0" encoding="ISO-8859-1"?>  
      <root>  
        
        <node att="a b c" />  
        <node att="a c b" />  
        <node att="a bb c" />  
        <node att="b c d" />  
        <node att="ab cb" />  
        
      </root>
      

      ergibt sich: true true false true false ('b' wird bei true also jeweils gefunden).

      Man liest sich,
      svg4you