XSLT -Vertiefung der Inhaltsstruktur
-
22-09-2019 - |
Frage
Angesichts der folgenden Struktur:
<TITEL1>...</TITEL1>
<p>..</p>
<TITEL2>...</TITEL2>
<TITEL3>...</TITEL3>
<TITEL3>...</TITEL3>
<P>...<P>
Gibt es eine Möglichkeit, dazu zu gelangen:
<TITEL1>
<TITEL>...</TITEL>
<p>...</p>
<TITEL2>
<TITEL>...</TITEL>
<TITEL3>
<TITEL>...</TITEL>
<P>...</P>
</TITEL3>
<TITEL3>
<TITEL>...</TITEL>
<P>...</P>
</TITEL3>
</TITEL2>
</TITEL1>
Oder mit anderen Worten, gibt es eine Möglichkeit, Titels mit höherer Ebene auf niedrigere Titels und alle ihnen folgenen Inhalte einzubeziehen, um eine verschachtelte Struktur zu erzeugen. Der Inhalt jedes Tenns von Titel1, 2 und 3 sollte in eine neue werden <TITEL>
-Element
Lösung
Mit XSLT 2.0 (wie von Saxon 9 oder AltovaxML-Tools implementiert) können Sie XSL verwenden: For-Each-Gruppen-Gruppen-Starting und eine rekursive Funktion:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/2010/mf"
exclude-result-prefixes="xsd mf">
<xsl:output indent="yes"/>
<xsl:function name="mf:nest" as="element()*">
<xsl:param name="elements" as="element()*"/>
<xsl:param name="level" as="xsd:integer"/>
<xsl:for-each-group select="$elements" group-starting-with="*[starts-with(local-name(), concat('TITEL', $level))]">
<xsl:choose>
<xsl:when test="self::*[starts-with(local-name(), concat('TITEL', $level))]">
<xsl:element name="TITEL{$level}">
<xsl:apply-templates select="."/>
<xsl:sequence select="mf:nest(current-group() except ., $level + 1)"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="ROOT">
<xsl:sequence select="mf:nest(*, 1)"/>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(local-name(), 'TITEL')]">
<TITEL>
<xsl:apply-templates select="@* | node()"/>
</TITEL>
</xsl:template>
</xsl:stylesheet>
Mit diesem Stylesheet die Eingabe
<ROOT>
<TITEL1>Titel 1, 1</TITEL1>
<p>..</p>
<TITEL2>Titel 2, 1</TITEL2>
<TITEL3>Titel 3, 1</TITEL3>
<TITEL3>Titel 3, 2</TITEL3>
<P>...</P>
</ROOT>
wird in die Ausgabe transformiert
<TITEL1>
<TITEL>Titel 1, 1</TITEL>
<p>..</p>
<TITEL2>
<TITEL>Titel 2, 1</TITEL>
<TITEL3>
<TITEL>Titel 3, 1</TITEL>
</TITEL3>
<TITEL3>
<TITEL>Titel 3, 2</TITEL>
<P>...</P>
</TITEL3>
</TITEL2>
</TITEL1>
Andere Tipps
Es gibt keine besonders geeignete Art, das zu tun, was Sie wollen. Es ist (wahrscheinlich) möglich, aber es würde einige ziemlich hässliche (und langsame) XPath -Abfragen verwenden following-sibling
Achse mit Filtern auf der preceding-sibling
Achse zurück zum aktuellen Knoten.
Wenn es überhaupt eine Möglichkeit ist, würde ich empfehlen, die Hierarchie außerhalb von XSLT (in C#, Java usw.) zu erstellen.
Wenn Sie sich für den beängstigenden Weg entscheiden, möchten Sie so etwas tun (ungetestet):
<xsl:template match="TITEL1">
<TITEL1>
<xsl:apply-templates
select="following-sibling::(p|TITEL2)[(preceding-sibling::TITEL1)[1]=.]" />
</TITEL1>
</xsl:template>
<xsl:template match="TITEL2">
<TITEL1>
<xsl:apply-templates
select="following-sibling::(p|TITEL3)[(preceding-sibling::TITEL2)[1]=.]" />
</TITEL1>
</xsl:template>
...
Dies ist nur ein Beispiel, und ich kann bereits Probleme mit der Übereinstimmung sehen. Die endgültige XPath -Abfrage zu entwickeln, wäre ziemlich involviert, wenn es eigentlich überhaupt möglich ist.
Wenn Sie XSLT 2.0 nicht verwenden können, finden Sie hier ein XSLT 1.0 -Stylesheet, das das gleiche Ergebnis wie das zuvor gepostete XSLT 2.0 -Stylesheet erzielen sollte:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="ROOT">
<xsl:apply-templates select="*[1]" mode="nest">
<xsl:with-param name="level" select="1"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*[starts-with(local-name(), 'TITEL')]" mode="nest">
<xsl:param name="level"/>
<xsl:choose>
<xsl:when test="$level = substring-after(local-name(), 'TITEL')">
<xsl:element name="TITEL{$level}">
<xsl:apply-templates select="."/>
<xsl:apply-templates select="following-sibling::*[1][not(starts-with(local-name(), concat('TITEL', $level)))]" mode="nest">
<xsl:with-param name="level" select="$level"/>
</xsl:apply-templates>
</xsl:element>
<xsl:apply-templates select="following-sibling::*[starts-with(local-name(), concat('TITEL', $level))][1]" mode="nest">
<xsl:with-param name="level" select="$level"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="." mode="nest">
<xsl:with-param name="level" select="$level + 1"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="*[not(starts-with(local-name(), 'TITEL'))]" mode="nest">
<xsl:param name="level"/>
<xsl:apply-templates select="."/>
<xsl:apply-templates select="following-sibling::*[1][not(starts-with(local-name(), concat('TITEL', $level)))]" mode="nest">
<xsl:with-param name="level" select="$level"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(local-name(), 'TITEL')]">
<TITEL>
<xsl:apply-templates select="@* | node()"/>
</TITEL>
</xsl:template>
</xsl:stylesheet>