xsl | Elemente filtern und zusammenführen
JanineS
- xsl
Hallo zusammen,
ich habe ein XSL-Problem und bereits vieles probiert, komme aber nicht auf die Lösung. Dennoch habe ich das Gefühl, dass es einfach zu lösen sein muss. Frustrierend -.- Vielleicht könnt ihr mir ja helfen...
Ich habe folgende Quelldatei:
<roles>
<role>
<name>A</name>
<part>1</part>
</role>
<role>
<name>A</name>
<part>2</part>
</role>
<role>
<name>B</name>
<part>1</part>
</role>
</roles>
Und möchte in der Ausgabe die <name> filtern, sodass jeder Name nur 1x vorkommt, aber dafür alle <part> zusammenführen. Also in Textform so etwas: A: 1, 2 B: 1
Ich habe es mit preceding::sibling probiert, mit Variablen. Konnte keine Arrays ins XSL finden und bin nun überfragt. Habt ihr Ideen, die mich zu einer Lösung bringen könnten?
Besten Dank schonmal, Janine
Hallo Janine,
Und möchte in der Ausgabe die <name> filtern, sodass jeder Name nur 1x vorkommt, aber dafür alle <part> zusammenführen. Also in Textform so etwas: A: 1, 2 B: 1
Probiere es mit diesem 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:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="roles">
<xsl:variable name="names" select="fn:distinct-values(role/name)" as="xs:string*"/>
<xsl:variable name="roles" select="role" as="node()*"/>
<roles>
<xsl:for-each select="$names">
<xsl:variable name="name" select="."/>
<role>
<name>
<xsl:value-of select="$name"/>
</name>
<part>
<xsl:value-of select="fn:concat($name, ': ')"/>
<xsl:for-each select="$roles/name[. = $name]">
<xsl:value-of select="following-sibling::part"/>
<xsl:if test="fn:position() != fn:last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</part>
</role>
</xsl:for-each>
</roles>
</xsl:template>
</xsl:stylesheet>
Ergebnis:
<?xml version="1.0" encoding="UTF-8"?>
<roles>
<role>
<name>A</name>
<part>A: 1, 2</part>
</role>
<role>
<name>B</name>
<part>B: 1</part>
</role>
</roles>
Grüße,
Thomas
Hallo Thomas,
besten Dank für deine schnelle Hilfe! Ich kam bisher mit den doppelten Schleifen nicht klar. Dank deiner Lösung konnte ich meinen ursprünglichen Code noch etwas umschreiben und bin nun zu der Lösung noch gekommen:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="roles">
<roles>
<xsl:for-each select="//role[not(text()=preceding-sibling::role/text())]">
<xsl:variable name="name" select="name"/>
<xsl:variable name="part" select="//role[name=$name]/part"/>
<role>
<name>
<xsl:value-of select="$name"/>
</name>
<part>
<xsl:value-of select="$part"/>
</part>
</role>
</xsl:for-each>
</roles>
</xsl:template>
</xsl:stylesheet>
Ausgabe ist dann:
<?xml version="1.0" encoding="UTF-8"?>
<roles xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<role>
<name>A</name>
<part>1 2</part>
</role>
<role>
<name>A</name>
<part>1 2</part>
</role>
<role>
<name>B</name>
<part>1</part>
</role>
</roles>
Viele Grüße JanineS
Hallo Janine,
Ausgabe ist dann:
<?xml version="1.0" encoding="UTF-8"?> <roles xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <role> <name>A</name> <part>1 2</part> </role> <role> <name>A</name> <part>1 2</part> </role> <role> <name>B</name> <part>1</part> </role> </roles>
Sieht für mich aber nicht ganz wie in der Frage aus: "sodass jeder Name nur 1x vorkommt". Aber ok, wenn so gewollt.
Hinweis: Wenn kein Zugriff auf die Namensräume fn bzw. xs erfolgt, können diese auch aus dem Stylesheet entfernt werden oder eben wie gezeigt mit exclude-result-prefixes="fn xs" | alle ="#all" von der Ausgabe ausschließen.
Grüße,
Thomas
Hallo Thomas,
du hast Recht, da habe ich den Wald vor lauter Bäumen übersehen. Meine ursprüngliche Datei ist wesentlich komplexer. Danke für den Hinweis. Da hat in der xsl:for-each nur ein name-Element gefehlt:
<xsl:for-each select="//role[not(name/text()=preceding-sibling::role/name/text())]">
LG JanineS