XPath 3.0, XPath 2.0, XPath 1.0 / XPath-Funktionen / XPath: Sequenz-Funktionen / XPath: collection

XPath: collection

XPath: collection

➪ Die XPath-2.0-Funktion collection ermöglicht, mehrere XML-Dateien als Input anzusprechen und deren jeweiligen Dateinamen über base-uri zu ermitteln. In XPath 3.0 steht die uri-collection bereit, die erlaubt, eine Collection auch von Nicht-XML-Dateien (z.B. CSV) anzusprechen und auszuwerten.

Auf dieser Seite:

Ein einfaches Beispiel stellt die gemeinsame Verarbeitung von XML-Dateien dar, die eine gemeinsame Struktur aufweisen, also beispielsweise gegen ein validiert werden können.

Freilich unterstützen nicht alle die komfortable collection-Funktion. Die Beispiele wurden mithilfe des Saxon-9-Prozessors erstellt.

Das folgende Beispiel beruht auf XML-Dateien, die ich vorher via generiert habe.


<xsl:stylesheet 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
     version="2.0">    
  <xsl:template match="/">
    <html>
      <body>
        <xsl:for-each 
             select="collection('../output2/?select=*.xml;recurse=no')">
          <h1>
            <xsl:text>Ergebnisse für </xsl:text>
            <xsl:value-of select="Umsatz/@artikel"/>
            <xsl:text> aus </xsl:text>
            <xsl:value-of select="Umsatz/@ort"/>
          </h1>
          <table>
            <xsl:for-each select="Umsatz/Kunde">
              <tr>
                <td>
                  <xsl:value-of select="@VN"/>
                </td>
                <td>
                  <xsl:value-of select="@NN"/>
                </td>
                <td>
                  <xsl:value-of select="anzahl"/>
                </td>
              </tr>
            </xsl:for-each>
          </table>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

XSL-Analyse mit Collections

Das folgende XSL-Stylesheet listet sämtliche Dateien eines Startverzeichnisses auf, die einem bestimmten Pattern entsprechen. Im vorliegenden Fall geht es um die gezielte Suche von XSL-Dateien nach dem Suchmuster ausgelagert*.xsl.


<xsl:template match="/">
  <gefundeneDateien zeit="{current-dateTime()}">
    <xsl:for-each
      select="collection(
      'file:/C:/wg/xsl/?select=ausgelagert*.xsl;recurse=yes')">
      <xsl:variable name="vfilename" select="base-uri()" />
      <datei filename="{$vfilename}"/>
    </xsl:for-each>
  </gefundeneDateien>
</xsl:template>

Das Suchergebnis lautet:


<gefundeneDateien zeit="2018-03-12T10:41:54.58+01:00">
  <datei filename="file:/C:/wg/xsl/ausgelagert1.xsl"/>
  <datei filename="file:/C:/wg/xsl/ausgelagert2.xsl"/>
  <datei filename="file:/C:/wg/xsl/ausgelagert3.xsl"/>
</gefundeneDateien>

Mehrfach-Implementierungen

Bei unkoordinierter Arbeitsweise kann es passieren, dass diverse Teillogiken (Templates, Funktionen, Variablen, Attribute-Sets) in verschiedenen XSL-Dateien unter demselben Namen mehrfach implementiert und mit in ein neues XSL-Stylesheet eingebunden werden. Mit der XPath-Funktion collection ist es möglich, Deklarationskonflikte zu ermitteln. Die drei vorher gefundenen Dateien haben diesen Aufbau:


Datei: ausgelagert1.xsl
<xsl:stylesheet 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">
  <xsl:variable name="v1">
    <Person nachname="Holzflos" vorname="Hugo"/>
  </xsl:variable>
  <xsl:attribute-set name="myattset">
    <xsl:attribute name="wert1">wert1</xsl:attribute>
    <xsl:attribute name="wert2">wert2</xsl:attribute>
  </xsl:attribute-set>
  <xsl:template name="ausgeben">
    <xsl:param name="p"/>
    <p xsl:use-attribute-sets="myattset">
      <xsl:value-of select="$p"/>
      <xsl:text>:</xsl:text>
      <xsl:value-of select="$v1"/>
    </p>
  </xsl:template>
  <xsl:template match="vorname">
    <b>
      <xsl:value-of select="."/>
    </b>
  </xsl:template>
</xsl:stylesheet>

ausgelagert2.xsl arbeitet teilweise mit denselben Templates, Variablen, Attribute-Sets, die jedoch eine andere Implementierung im Detail umfassen.


Datei: ausgelagert2.xsl
<xsl:stylesheet 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:f="funktionsbeispiel"
  version="2.0">
  <xsl:param name="p"/>
  <xsl:function name="f:addiere">
    <xsl:param name="a"/>
    <xsl:param name="b"/> 
    <xsl:sequence select="$a + $b"/>
  </xsl:function>
  <xsl:variable name="eineandereVariable">
    <xsl:variable name="v1">
      <Person nachname="Wunschlos" vorname="Wilma"/>
    </xsl:variable>
  </xsl:variable>
  <xsl:attribute-set name="myattset">
    <xsl:attribute name="wert1">wert3</xsl:attribute>
    <xsl:attribute name="wert2">wert4</xsl:attribute>
  </xsl:attribute-set>
  <xsl:template name="ausgeben">
    <xsl:param name="p"/>
    <p xsl:use-attribute-sets="myattset">
      <xsl:variable name="v1">
        <Person nachname="Achtlos" vorname="Anja"/>
      </xsl:variable>
      <xsl:value-of select="$v1"/>
      <xsl:text>:</xsl:text>
      <xsl:value-of select="$p"/>
    </p>
  </xsl:template>
  <xsl:template match="vorname">
    <i>
      <xsl:value-of select="."/>
    </i>
  </xsl:template>
</xsl:stylesheet>

ausgelagert3.xsl verwendet abermals Templates, Variablen, Attribute-Sets, die teilweise bereits in den anderen XSL-Stylesheets deklariert wurden, mit nochmals anderer Implementierung.


Datei: ausgelagert3.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:f="funktionsbeispiel"
  version="2.0">
  <xsl:param name="p"/>
  <xsl:function name="f:addiere">
    <xsl:param name="p"/>
    <xsl:param name="b"/> 
    <xsl:sequence select="$p + $b"/>
  </xsl:function>
  <xsl:template name="ausgeben">
    <xsl:param name="p"/>
    <xsl:variable name="v1">
      <Person nachname="Lautlos" vorname="Lars"/>
    </xsl:variable>
    <p>
      <xsl:value-of select="$p"/>
      <xsl:text>:</xsl:text>
      <xsl:value-of select="$v1"/>
    </p>
  </xsl:template>
</xsl:stylesheet>

Zur Analyse, in welchen externen XSL-Dateien Teillogiken implementiert sind, bietet sich die collection-Funktion an. Im folgenden Beispiel durchsuche ich alle Dateien, die einem bestimmten Pattern entsprechen, nach allen global definierten xsl:template, xsl:attribute-set sowie xsl:variable (hier beziehe ich auch lokal definierte Variablen ein), und schreibe das Ergebnis in eine temporäre Variable vcollection.


<xsl:variable name="vcollection">
  <gefundeneDateien zeit="{current-dateTime()}">
    <xsl:for-each
      select="collection(
      'file:/C:/wg/xsl/?select=ausgelagert*.xsl;recurse=yes')">
      <xsl:variable name="vfilename" select="base-uri()" />
      <datei filename="{$vfilename}">
        <xsl:for-each 
          select="/*/descendant-or-self::xsl:template[@name]">
          <TEMPLATE name="{@name}" />
        </xsl:for-each>
        <xsl:for-each 
          select="/*/descendant-or-self::xsl:template[@match]">
          <TEMPLATE match="{@match}" />
        </xsl:for-each>
        <xsl:for-each select="/*/descendant-or-self::xsl:attribute-set">
          <ATTRIBUTESET name="{@name}" />
        </xsl:for-each>
        <xsl:for-each select="/*/descendant-or-self::xsl:function">
          <FUNCTION name="{@name}" />
        </xsl:for-each>
        <xsl:for-each select="/*/descendant-or-self::xsl:variable">
          <VARIABLE name="{@name}">
            <xsl:attribute name="XPath">
              <xsl:for-each select="ancestor-or-self::*">
                <xsl:text>/</xsl:text>
                <xsl:value-of select="local-name()"/>
              </xsl:for-each>
            </xsl:attribute>
          </VARIABLE>
        </xsl:for-each>
        <xsl:for-each select="/*/descendant-or-self::xsl:param">
          <PARAMETER name="{@name}">
            <xsl:attribute name="XPath">
              <xsl:for-each select="ancestor-or-self::*">
                <xsl:text>/</xsl:text>
                <xsl:value-of select="local-name()"/>
              </xsl:for-each>
            </xsl:attribute>
          </PARAMETER>
        </xsl:for-each>
      </datei>
    </xsl:for-each>
  </gefundeneDateien>
</xsl:variable>

Den Inhalt von vcollection, die immerhin die Liste aller gefundenen Dateien sowie die darin gefundenen Variablen enthält, sehen Sie, wenn Sie sich die Variable ausgeben lassen.


<gefundeneDateien zeit="2018-03-12T10:56:59.312+01:00">
  <datei filename="file:/C:/wg/xsl/ausgelagert1.xsl">
   <TEMPLATE name="ausgeben"/>
   <TEMPLATE match="vorname"/>
   <ATTRIBUTESET name="myattset"/>
   <VARIABLE name="v1" XPath="/stylesheet/variable"/>
   <PARAMETER name="p" XPath="/stylesheet/template/param"/>
  </datei>
  <datei filename="file:/C:/wg/xsl/ausgelagert2.xsl">
   <TEMPLATE name="ausgeben"/>
   <TEMPLATE match="vorname"/>
   <ATTRIBUTESET name="myattset"/>
   <FUNCTION name="f:addiere"/>
   <VARIABLE name="eineandereVariable" XPath="/stylesheet/variable"/>
   <VARIABLE name="v1" XPath="/stylesheet/variable/variable"/>
   <VARIABLE name="v1" XPath="/stylesheet/template/p/variable"/>
   <PARAMETER name="p" XPath="/stylesheet/param"/>
   <PARAMETER name="a" XPath="/stylesheet/function/param"/>
   <PARAMETER name="b" XPath="/stylesheet/function/param"/>
   <PARAMETER name="p" XPath="/stylesheet/template/param"/>
  </datei>
  <datei filename="file:/C:/wg/xsl/ausgelagert3.xsl">
   <TEMPLATE name="ausgeben"/>
   <FUNCTION name="f:addiere"/>
   <VARIABLE name="v1" XPath="/stylesheet/template/variable"/>
   <PARAMETER name="p" XPath="/stylesheet/param"/>
   <PARAMETER name="p" XPath="/stylesheet/function/param"/>
   <PARAMETER name="b" XPath="/stylesheet/function/param"/>
   <PARAMETER name="p" XPath="/stylesheet/template/param"/>
  </datei>
</gefundeneDateien>

Nunmehr geht es darum, die Inhalte der temporären Variable vcollection durch interne Gruppierungen auszuwerten.


<xsl:template match="/">   
  <DoppelteWerte 
    SucheVon="{$vcollection/gefundeneDateien/@zeit}">
    <xsl:for-each-group 
      select="$vcollection/gefundeneDateien/datei/ATTRIBUTESET[@name]" 
      group-by="@name">
      <xsl:call-template name="t_ausgabe"/>
    </xsl:for-each-group>
    <xsl:for-each-group 
      select="$vcollection/gefundeneDateien/datei/VARIABLE" 
      group-by="@name">
      <xsl:call-template name="t_ausgabe"/>
    </xsl:for-each-group>
    <xsl:for-each-group 
      select="$vcollection/gefundeneDateien/datei/PARAMETER" 
      group-by="@name">
      <xsl:call-template name="t_ausgabe"/>
    </xsl:for-each-group>
    <xsl:for-each-group 
      select="$vcollection/gefundeneDateien/datei/FUNCTION" 
      group-by="@name">
      <xsl:call-template name="t_ausgabe"/>
    </xsl:for-each-group>
    <xsl:for-each-group 
      select="$vcollection/gefundeneDateien/datei/TEMPLATE[@name]" 
      group-by="@name">
      <xsl:call-template name="t_ausgabe"/>
    </xsl:for-each-group>
    <xsl:for-each-group 
      select="$vcollection/gefundeneDateien/datei/TEMPLATE[@match]" 
      group-by="@match">
      <xsl:call-template name="t_ausgabe"/>
    </xsl:for-each-group>
  </DoppelteWerte>
</xsl:template>

Die vorstehende Logik ruft wiederholt ein Template t_ausgabe auf:


<xsl:template name="t_ausgabe">    
  <xsl:if test="count(current-group()) &gt; 1">
    <xsl:element name="{local-name()}">
      <xsl:attribute 
           name="{if (@name) then ('name') else ('match')}" 
           select="current-grouping-key()"/>
      <xsl:for-each select="current-group()">
        <Datei>
          <xsl:if test="@XPath">
            <xsl:attribute name="XPath" select="@XPath"/>
          </xsl:if>
          <xsl:value-of select="../@filename"/>
        </Datei>
      </xsl:for-each>      
    </xsl:element>
  </xsl:if>
</xsl:template>

Das Ergebnis listet die mehrfach deklarierten Variablen, Attribute-Sets, Functions, Parameter, Templates in allen gefundenen Dateien auf, deren Namen dem beschriebenen Pattern ausgelagert*.xsl entsprechen. Dabei ist noch offen, ob es sich um eine identische oder unterschiedliche Implementierung handelt.


<DoppelteWerte SucheVon="2018-03-12T11:00:07.161+01:00">
  <ATTRIBUTESET name="myattset">
   <Datei>
          file:/C:/wg/xsl/ausgelagert1.xsl
   </Datei>
   <Datei>
          file:/C:/wg/xsl/ausgelagert2.xsl
   </Datei>
  </ATTRIBUTESET>
  <VARIABLE name="v1">
   <Datei XPath="/stylesheet/variable">
          file:/C:/wg/xsl/ausgelagert1.xsl
   </Datei>
   <Datei XPath="/stylesheet/variable/variable">
          file:/C:/wg/xsl/ausgelagert2.xsl
   </Datei>
   <Datei XPath="/stylesheet/template/p/variable">
          file:/C:/wg/xsl/ausgelagert2.xsl
   </Datei>
   <Datei XPath="/stylesheet/template/variable">
          file:/C:/wg/xsl/ausgelagert3.xsl
   </Datei>
  </VARIABLE>
  <PARAMETER name="p">
   <Datei XPath="/stylesheet/template/param">
          file:/C:/wg/xsl/ausgelagert1.xsl
   </Datei>
   <Datei XPath="/stylesheet/param">
          file:/C:/wg/xsl/ausgelagert2.xsl
   </Datei>
   <Datei XPath="/stylesheet/template/param">
          file:/C:/wg/xsl/ausgelagert2.xsl
   </Datei>
   <Datei XPath="/stylesheet/param">
          file:/C:/wg/xsl/ausgelagert3.xsl
   </Datei>
   <Datei XPath="/stylesheet/function/param">
          file:/C:/wg/xsl/ausgelagert3.xsl
   </Datei>
   <Datei XPath="/stylesheet/template/param">
          file:/C:/wg/xsl/ausgelagert3.xsl
   </Datei>
  </PARAMETER>
  <PARAMETER name="b">
   <Datei XPath="/stylesheet/function/param">
          file:/C:/wg/xsl/ausgelagert2.xsl
   </Datei>
   <Datei XPath="/stylesheet/function/param">
          file:/C:/wg/xsl/ausgelagert3.xsl
   </Datei>
  </PARAMETER>
  <FUNCTION name="f:addiere">
   <Datei>file:/C:/wg/xsl/ausgelagert2.xsl</Datei>
   <Datei>file:/C:/wg/xsl/ausgelagert3.xsl</Datei>
  </FUNCTION>
  <TEMPLATE name="ausgeben">
   <Datei>file:/C:/wg/xsl/ausgelagert1.xsl</Datei>
   <Datei>file:/C:/wg/xsl/ausgelagert2.xsl</Datei>
   <Datei>file:/C:/wg/xsl/ausgelagert3.xsl</Datei>
  </TEMPLATE>
  <TEMPLATE match="vorname">
   <Datei>file:/C:/wg/xsl/ausgelagert1.xsl</Datei>
   <Datei>file:/C:/wg/xsl/ausgelagert2.xsl</Datei>
  </TEMPLATE>
</DoppelteWerte>

uri-collection: Auflistung von Nicht-XML-Dateien

Ein Problem ist die Arbeit mit der XPath-2.0-collection-Funktion, wenn es sich bei den gesuchten Dateien nicht um XML-Dokumente handelt, sondern beispielsweise um -Dateien. Wenn Sie also vor der Aufgabe stehen, mit XSLT alle CSV-Dateien zu listen, so können Sie unter XSLT 3.0/ XPath 3.0 gut mit der uri-collection arbeiten.


<xsl:for-each 
     select="uri-collection('file:/C:/wg/input?select=*.csv')">
    <datei name="{iri-to-uri(.)}">
      <xsl:value-of select="unparsed-text(.)"/>
  </datei>  
</xsl:for-each>

wg / 15. September 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/collections.html