Gern stehe ich zur fachlichen Unterstützung in XML-Technologien, C#.NET, VisualBasic.NET und Java zur Verfügung. Sprechen Sie mich einfach an: Mail oder ☎ 0151 . 750 360 61


XPath 3.0, XPath 2.0, XPath 1.0 / XPath-Funktionen / XPath: Sequenz-Funktionen / XPath: distinct-values

XPath: distinct-values

XPath: distinct-values

➪ Die XPath-Funktion distinct-values löscht mehrfache identische Einträge in einer Sequenz und reduziert die Ausgabe auf Einzelresultate.

Auf dieser Seite:

Wenn es darum geht, mehrfach vorkommende Einzelwerte zu , also so zusammenzufassen, dass sie jeweils nur einmal auftreten, steht auch die Funktion distinct-values zur Verfügung.


<xsl:for-each select="distinct-values(//Kauf/bez)">
    <Artikel name="{.}"/>
</xsl:for-each>

Als Ergebnis finden Sie:


<Artikel name="Hemd"/>
<Artikel name="Hose"/>
<Artikel name="Schuhe"/>

Freilich geht in diesem Moment der Zugriff auf die Datenquelle verloren:


<xsl:for-each select="distinct-values(//Kauf/bez)">
    <xsl:variable name="vartikel" select="."/>
    <Artikel name="{$vartikel}">
        <xsl:for-each select="//Mensch[Kauf/bez=$vartikel]">
            <kunde/>
        </xsl:for-each>
    </Artikel>
</xsl:for-each>

Diesen Aufruf würde der XSL-Prozessor mit einem netten Gruß aus der Beschwerdeabteilung quittieren.


Fehlerlevel: fatal
XPTY0020: Leading '/' 
         cannot select the root node 
         of the tree containing the 
         context item: the context 
         item is not a node
URL: http://www.w3.org/TR/xpath20/#ERRXPTY0020

Ein suboptimaler, aber dennoch funktionierender Ausweg wäre, den gesamten XML-Input in eine Variable zu verpacken und die weitere Verarbeitung auf dieser Variablen zu gründen.


<xsl:variable name="vinput" select="."/>
<xsl:for-each select="distinct-values(//Kauf/bez)">
    <xsl:variable name="vartikel" select="."/>
    <Artikel name="{$vartikel}">
        <xsl:for-each 
             select="$vinput//Mensch[Kauf/bez=$vartikel]">
            <kunde nn="{name}" 
                   vn="{vorname}" 
                   wo="{../name}"/>
        </xsl:for-each>
    </Artikel>
</xsl:for-each>

Gruppieren nach Elementnamen

Sie können distinct-values auch dazu verwenden, nach Elementnamen zu gruppieren. Nehmen Sie dieses XML-Dokument als Input:


<Personen>
    <Person>
        <Nachname>Sorglos</Nachname>
        <Vorname>Siggi</Vorname>
        <Geburtsjahr>1981</Geburtsjahr>
    </Person>
    <Person>
        <Nachname>Wunschlos</Nachname>
    </Person>
</Personen>

Sie interessieren sich dafür, welche Elementnamen unterhalb Person vorhanden sind?


<Elementnamen>
   <eName>Nachname</eName>
   <eName>Vorname</eName>
   <eName>Geburtsjahr</eName>
</Elementnamen>

In XQuery können Sie so vorgehen:


<Elementnamen>
  {
    for $x in 
        distinct-values(/Personen/Person/child::*/name(.))
    return
      <eName>{$x}</eName>
  }
</Elementnamen>

In XSLT 2.0 sieht das ganz ähnlich aus:


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="2.0">
<xsl:output method="xml" indent="yes"></xsl:output>
  <xsl:template match="/">
    <Elementnamen>
    <xsl:for-each 
         select="distinct-values(/Personen/Person/child::*/name(.))">
      <eName>
       <xsl:value-of select="."/> 
      </eName>
    </xsl:for-each>
    </Elementnamen>
  </xsl:template>
</xsl:stylesheet>

Entfernen redundanter komplexer Elemente

Suboptimal ist die Arbeit mit distinct-values, wenn es um das Entfernen redundanter komplexer Elemente geht. Betrachten Sie dieses XML-Dokument, in dem Elemente mit demselben Eintrag mehrfach enthalten sind.


<Personen>
    <Person>
        <Nachname>Sorglos</Nachname>
        <Vorname>Siggi</Vorname>
        <Geburtsjahr>1981</Geburtsjahr>
    </Person>
    <Person>
        <Nachname>Wunschlos</Nachname>
        <Vorname>Wilma</Vorname>
        <Geburtsjahr>1985</Geburtsjahr>
    </Person>
    <Person>
        <Nachname>Sorglos</Nachname>
        <Vorname>Siggi</Vorname>
        <Geburtsjahr>1981</Geburtsjahr>
    </Person>
    <Person>
        <Nachname>Wunschlos</Nachname>
        <Vorname>Wilma</Vorname>
        <Geburtsjahr>1985</Geburtsjahr>
    </Person>
    <Person>
        <Nachname>Sorglos</Nachname>
        <Vorname>Siggi</Vorname>
        <Geburtsjahr>1981</Geburtsjahr>
    </Person>
</Personen>

Ihr Ziel ist es, die doppelten Einträge zu löschen, übrig bleiben sollen:


<Personen>
   <Person>
        <Nachname>Sorglos</Nachname>
        <Vorname>Siggi</Vorname>
        <Geburtsjahr>1981</Geburtsjahr>
    </Person>
   <Person>
        <Nachname>Wunschlos</Nachname>
        <Vorname>Wilma</Vorname>
        <Geburtsjahr>1985</Geburtsjahr>
    </Person>
</Personen>

distinct-values bringt hier zwar ein Ergebnis, ...


<Personen>
  <xsl:for-each 
       select="distinct-values(/Personen/Person)">
    <Person>
      <xsl:value-of select="."/>
    </Person>
  </xsl:for-each>
</Personen>  

... aber kaum das gewünschte:


<Personen>
   <Person>
        Sorglos
        Siggi
        1981
    </Person>
   <Person>
        Wunschlos
        Wilma
        1985
    </Person>
</Personen>

Zielführender ist dagegen die Arbeit mit not(. = preceding::Person/.). Siehe hierzu auch .


<Personen>
  <xsl:for-each 
       select="/Personen/Person[not(. = preceding::Person/.)]">
    <xsl:copy-of select="."/>
  </xsl:for-each>
</Personen>

Ähnlich elegant sieht das in XQuery aus:


<Personen>
  {
    for $x in /Personen/Person[not(. = preceding::Person/.)]
    return $x    
  }
</Personen>

wg / 15. November 2018



Fragen? Anmerkungen? Tipps?

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/distinctvalues.html