XPath 3.0, XPath 2.0, XPath 1.0 / XPath-Funktionen / Stringfunktionen / XPath: string-join

XPath: string-join

XPath: string-join

➪ Bei der XPath-Funktion string-join kann eine Sequenz als Parameter übergeben werden, die dann mithilfe eines Trennzeichens in eine Zeichenkette konvertiert wird.

Die string-join-Funktion leistet gegenüber der -Funktion einiges mehr: Hier ist es auch möglich, eine komplette Sequenz als Parameter zu übergeben. string-join ist quasi der Gegenentwurf von .

string-join-basierter Vergleich unterschiedlicher XML-Strukturen

Die string-join-Funktion leistet beim Vergleich verschiedener XML-Dokumente bzw. temporärer XML-Bäume gute Hilfe. Nehmen Sie an, Sie wollten zwei XML-Bäume unterschiedlicher Struktur daraufhin überprüfen, ob beide dieselben Inhalte aufweisen.

Das erste XML-Dokument (hier dargestellt als Variable, es könnte ebenso ein externes XML-Dokument sein) besteht aus mehreren ineinander verschachtelten info-Elementen, die jeweils ein eindeutiges Attribut content sowie darunter beliebig viele detail-Childnodes enthalten.


<xsl:variable name="v1">
    <info content="1">
        <detail>Wilma Wunschlos</detail>
        <info content="2">
            <detail>Theo Tadellos</detail>
            <detail>Helga Haltlos</detail>
            <info content="3">
                <detail>Liane Leinenlos</detail>
                <detail>Wolle Wolkenlos</detail>
            </info>
        </info>
        <info content="4">
            <detail>Anton Achtlos</detail>
            <info content="5">
                <detail>Siggi Sinnlos</detail>
                <info content="6">
                    <detail>Hugo Holzflos</detail>
                    <info content="7">
                        <detail>Lotte Rielos</detail>
                        <detail>Alfons Achtlos</detail>
                    </info>
                </info>
            </info>
        </info>
    </info>
</xsl:variable>

Der zweite XML-Baum (ebenfalls als Variable dargestellt) hat ein root-Element mit mehreren Childnodes ds, die jeweils exakt einen Childnode level und beliebig viele Childnodes Kandidat aufweisen. Die Inhalte der ds-Childnodes sollen mit der vorstehend beschriebenen Variable v1 verglichen werden.


<xsl:variable name="v2">
    <root>
        <ds>
            <level>1</level>
            <Kandidat>Wilma Wunschlos</Kandidat>
        </ds>
        <ds>
            <level>2</level>
            <Kandidat>Theo Tadellos</Kandidat>
            <Kandidat>Helga Haltlos</Kandidat>
        </ds>                      
        <ds>
            <level>3</level>
            <Kandidat>Liane Leinenlos</Kandidat>
            <Kandidat>Wolle Wolkenlos</Kandidat>
        </ds>
        <ds>
            <level>4</level>
            <Kandidat>Anton Achtlos</Kandidat>
        </ds>
        <ds>
            <level>5</level>
            <Kandidat>Siggi Sinnlos</Kandidat>
        </ds>
        <ds>
            <level>6</level>
            <Kandidat>Hugo Holzflos</Kandidat>
        </ds>
        <ds>
            <level>7</level>
            <Kandidat>Lotte Rielos</Kandidat>
            <Kandidat>Alfons Achtlos</Kandidat>
        </ds>
    </root>
</xsl:variable>

Der string-join-basierte Vergleich ist in XSLT 2.0 möglich durch eine Abarbeitung aller -Nodes, deren Inhalte via string-join in einer -Variable v1Info gesichert werden. Diese v1Info-Variable wird anschließend mit verglichen.


<xsl:template match="/">
  <ergebnis>
    <xsl:for-each select="$v1/descendant-or-self::info">
      <xsl:variable name="v1Info" as="xs:string">
        <xsl:value-of select="string-join((@content | detail), '|')"/>
      </xsl:variable>
      <xsl:variable name="vlevel" select="@content"/>
      <gesucht>
        <xsl:choose>
          <xsl:when 
             test="count
              (
               $v2/root/ds[string-join(
                   (child::level | child::Kandidat), '|') = $v1Info]
              ) = 0 ">
            <xsl:attribute name="Abweichung">
              <xsl:value-of 
                 select="$v2/root/ds[level=$vlevel]/
                  string-join(
                  (child::level | child::Kandidat), '|'
                  )"/>
            </xsl:attribute>
            <xsl:attribute name="nicht_gefunden">
              <xsl:value-of select="$v1Info"/>
            </xsl:attribute>
          </xsl:when>
          <xsl:otherwise>
            <xsl:attribute name="gefunden">
              <xsl:value-of select="$v1Info"/>
            </xsl:attribute>              
          </xsl:otherwise>
        </xsl:choose>          
      </gesucht>
    </xsl:for-each>
  </ergebnis>
</xsl:template>

Alternativ können Sie die xsl:when auch mit schreiben:


<xsl:when test="every $m in 
   $v2/root/ds[
     string-join((child::level | child::Kandidat), '|') 
     = $v1Info]
   satisfies compare(xs:string($m), $v1Info) =0">

Das Ergebnis lautet in beiden Fällen:


<ergebnis>
  <gesucht gefunden="1|Wilma Wunschlos"/>
  <gesucht gefunden="2|Theo Tadellos|Helga Haltlos"/>
  <gesucht gefunden="3|Liane Leinenlos|Wolle Wolkenlos"/>
  <gesucht gefunden="4|Anton Achtlos"/>
  <gesucht gefunden="5|Siggi Sinnlos"/>
  <gesucht gefunden="6|Hugo Holzflos"/>
  <gesucht gefunden="7|Lotte Rielos|Alfons Achtlos"/>
</ergebnis>

Lösung mit XQuery

Die beschriebene Lösung ist auch in XQuery umsetzbar.


<ergebnis>
{
  let $v1 := <info content="1">...</info>    
  let $v2 := <root>...</root>
  for $vm1 in $v1/descendant-or-self::info
    let $v1Info as xs:string := 
        string-join(($vm1/@content | $vm1/detail/text()), '|') 
    let $vlevel := $vm1/@content
    return <gesucht>{
      if (count($v2/descendant::ds[
        string-join(
            (child::level | child::Kandidat), '|')
        = $v1Info]) =0)
      then 
        attribute nicht_gefunden {$v1Info} |
        attribute Abweichung {
         for $mmm in $v2/descendant::ds
         where $mmm/level=$vlevel       
         return 
           string-join(
             ($mmm/level/text() | $mmm/Kandidat/text()), '|')
         }
      else attribute gefunden {$v1Info}
    }</gesucht>
}
</ergebnis>

string-join vs. xsl:value-of separator

Im folgenden Beispiel werden alle Mensch/name-Elemente des Input-Dokuments angesprochen und nur dann in die Sequenz aufgenommen, wenn sie einem vordefinierten (erster Buchstabe "S", anschliessend nur Kleinbuchstaben) entsprechen.


<xsl:variable name="y" as="xs:string+">
  <xsl:for-each 
       select="for $x in //Mensch/name/text() return 
               if ( matches($x, '[S][a-z]*')) 
               then $x 
               else nothing">
    <xsl:value-of select="."/>
  </xsl:for-each>
</xsl:variable>

Um einen Eindruck zu erhalten, was Sie da fabriziert haben, lohnt sich die Ausgabe der Variable $y in einer for-each-Schleife.


<Einzelwerte>
  <xsl:for-each select="$y">
    <wert><xsl:value-of select="."/></wert>
  </xsl:for-each>
</Einzelwerte>

Dessen Ausgabe sähe dann so aus:


<Einzelwerte>
  <wert>Sprachlos</wert>
  <wert>Sagblos</wert>
  <wert>Sorglos</wert>
  <wert>Sinnlos</wert>
  <wert>Schlaflos</wert>
</Einzelwerte>

Vergleichen Sie dieses Ergebnis mit jenem, das Sie bei Verwendung der string-join-Funktion bekommen.


<xsl:value-of select="string-join($y, '|')"/>

Ergebnis:


Sprachlos|Sagblos|Sorglos|Sinnlos|Schlaflos

Denselben Effekt bekommen Sie in diesem Fall auch auf andere Weise hin:


<xsl:value-of 
     select="//Mensch[matches(name, '[S][a-z]*')]/name" 
     separator="|"/>

Verknüpfungsoperatoren in XSL 3.0 / XPath 3.0

In XSL 3.0 / XPath 3.0 stehen besondere Verknüpfungsoperatoren zur Verfügung, die auch bei zum Einsatz kommen.


<xsl:value-of 
     select="string-join(('x' || 'y' || 'z'))"/>

liefert auch hier


<ergebnis>xyz</ergebnis>

Mehrere string-joins dieser Art ...


<xsl:value-of select="string-join((1 to 3)!('x' || 'y' || 'z'))"/>

bringen das hier ans Tageslicht:


<ergebnis>xyzxyzxyz</ergebnis>

Freilich bringt die Arbeit mit string-join eben auch nur einen einzelnen zustande, keine Sequenz wie bei .


 <xsl:template match="/">
  <ergebnis>
   <xsl:for-each 
        select="string-join((1 to 3)!('x' || 'y' || 'z'))">
    <einzelwert>
     <xsl:value-of select="."/>
    </einzelwert>
   </xsl:for-each>
  </ergebnis>
 </xsl:template>

Das erkennen Sie sofort am Ergebnis:


<ergebnis>
   <einzelwert>xyzxyzxyz</einzelwert>
</ergebnis>

Auch das funktioniert mit XQuery:


xquery version '3.0';
<ergebnis>
{
for $m in string-join((1 to 3)!('x' || 'y' || 'z'))
return <einzelwert>{$m}</einzelwert>
}
</ergebnis>

wg / 28. Juni 2018



Fragen? Anmerkungen? Tips?

Bitte nehmen Sie Kontakt zu mir auf.






Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/stringjoin.html