Elo: invertierten Text löschen

Hallo Forum,

ich habe folgende XML:

  
<?xml version="1.0" encoding="UTF-8"?>  
<root>  
    <data>eins, sieben, drei, vier</data>  
    <data>zwei, zwölf, sieben, acht</data>  
    <data>drei, vier, neun, sieben</data>  
    <data>sechs, zehn, elf</data>  
    <data>eins, neun, drei, sieben, sechs, zwei</data>  
    <data>zehn, zwölf, dreizehn, sieben, neun</data>  
    <data>drei</data>  
    <data>zwei, eins, sechs, neun, sieben, zehn</data>  
    <data>zwei, sieben</data>  
    <data>sechs, zehn, dreizehn</data>  
</root>  

Diese möchte ich gern filtern, so dass jene <data>-Elemente übrig bleiben, welche den String "eins" und/oder "sieben" enthalten. Soweit kein Problem.

Mein Problem, die <data>Elemente sollen danach nur entweder "eins", "sieben" oder "eins, sieben" enthalten - also der ungewünschte Text soll gelöscht werden.

Hier handelt es sich um ein einfaches Beispiel - aber man stelle sich vor, man hat hunderte Zahlen und möchte nach 10 filtern oder so...

Meine XSL:

  
<?xml version="1.0" encoding="UTF-8"?>  
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
    xmlns:xs="http://www.w3.org/2001/XMLSchema"  
    exclude-result-prefixes="xs"  
    version="2.0">  
  
    <xsl:strip-space elements="*"/>  
    <xsl:output indent="yes"/>  
  
    <xsl:template match="/">  
        <root>  
            <xsl:apply-templates/>  
        </root>  
    </xsl:template>  
  
    <xsl:template match="data">  
        <xsl:choose>  
            <xsl:when test="contains(., 'eins') or contains (.,'sieben')">  
                <xsl:copy>  
                    <xsl:apply-templates/>  
                </xsl:copy>  
            </xsl:when>  
            <xsl:otherwise/>  
        </xsl:choose>  
    </xsl:template>  
  
    <xsl:template match="text()">  
        <!-- ??? -->  
        <xsl:variable name="string">eins, sieben</xsl:variable>  
        <xsl:variable name="string_tok" select="tokenize($string, ',')"/>  
  
        <xsl:variable name="del_text">  
            <xsl:value-of select="replace(  
                                                            replace(., 'eins', ''),  
                                                            'sieben', '')"/>  
        </xsl:variable>  
        <xsl:value-of select="replace(., $del_text, '')"/>  
    </xsl:template>  
</xsl:stylesheet>  

Ich hatte nun den inversen Text mittels $del_text ermittelt. Dies sollte nun die Grundlage sein, um diesen zu replacen, was aber daran scheitert, dass dieser inverse Text nicht an einem Stück vorkommt. Ist eventl. eine Lösung mittels tokenize (darum steht es da) möglich?

ANMERKUNG: Die Lösung habe ich bereits, aber in einem zweistufigen Verfahren.
1. Template-Regel "data" filtert nach gewünschten Daten
2. Bilde Pseudo-Elemente aus "eins" und "sieben"

dann konnte ich im zweiten Schritt den text-node verwerfen und die Elemente wieder zu Text zurückführen.

Es wäre aber nice, eine Lösung in einem Schritt zu haben.

Grüße
Elo

  1. Hallo Elo,

    Mein Problem, die <data>Elemente sollen danach nur entweder "eins", "sieben" oder "eins, sieben" enthalten - also der ungewünschte Text soll gelöscht werden.

    XSLT-2.0-Ansatz:

    <?xml version="1.0" encoding="UTF-8"?>  
    <xsl:stylesheet version="2.0"  
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
      xmlns:xs="http://www.w3.org/2001/XMLSchema"  
      xmlns:fn="http://www.w3.org/2005/xpath-functions"  
      exclude-result-prefixes="fn xs">  
      
      <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>  
      
      <!-- Suchparameter kann ggf. von außen übergeben werden -->  
      <xsl:param name="search_txt" select="'eins,sieben'"/>  
      
      <xsl:variable name="search_seq" as="xs:string*">  
        <xsl:sequence select="fn:tokenize($search_txt,',')"/>  
      </xsl:variable>  
      
      <xsl:template match="root">  
        <root>  
          <xsl:apply-templates select="data"/>  
        </root>  
      </xsl:template>  
      
      <xsl:template match="data">  
        <xsl:variable name="data_out">  
          <xsl:sequence select="for $i in 1 to fn:count($search_seq)  
            return if(fn:contains(.,$search_seq[$i])) then $search_seq[$i] else()"/>  
        </xsl:variable>  
      
        <xsl:for-each select="$data_out">  
          <xsl:if test="fn:string-length(.) > 0">  
            <data><xsl:value-of select="fn:replace(.,' ',', ')"/></data>  
          </xsl:if>  
        </xsl:for-each>  
      </xsl:template>  
      
    </xsl:stylesheet>
    

    Ergebnis:

    <?xml version="1.0" encoding="UTF-8"?>  
    <root>  
       <data>eins, sieben</data>  
       <data>sieben</data>  
       <data>sieben</data>  
       <data>eins, sieben</data>  
       <data>sieben</data>  
       <data>eins, sieben</data>  
       <data>sieben</data>  
    </root>
    

    Grüße,
    Thomas

    1. Hallo Thomas,

      danke für die Hilfe. Es klappt wie gewünscht. Hier zeigt sich, dass meine Kenntnisse bzgl. XPath noch ausbaufähig sind.

      Grüße
      Elo