XSLT approfondisce la struttura dei contenuti
-
22-09-2019 - |
Domanda
data la seguente struttura:
<TITEL1>...</TITEL1>
<p>..</p>
<TITEL2>...</TITEL2>
<TITEL3>...</TITEL3>
<TITEL3>...</TITEL3>
<P>...<P>
c'è un modo per arrivare a questo:
<TITEL1>
<TITEL>...</TITEL>
<p>...</p>
<TITEL2>
<TITEL>...</TITEL>
<TITEL3>
<TITEL>...</TITEL>
<P>...</P>
</TITEL3>
<TITEL3>
<TITEL>...</TITEL>
<P>...</P>
</TITEL3>
</TITEL2>
</TITEL1>
o in altre parole, esiste un modo per fare in modo che i titoli di livello superiore racchiudano i titoli di livello inferiore e tutto il contenuto che li segue, creando così una struttura nidificata.Il contenuto di ciascun tag TITEL1,2 e 3 dovrebbe andare in un nuovo file <TITEL>
-elemento
Soluzione
Con XSLT 2.0 (come implementato dagli strumenti Saxon 9 o AltovaXML) puoi utilizzare xsl:for-each-group group-starting-with e una funzione ricorsiva:
<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>
Con quel foglio di stile l'input
<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>
viene trasformato in output
<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>
Altri suggerimenti
Non c'è un modo particolarmente Eligant di fare ciò che si vuole. E '(probabilmente) possibile, ma si tratterebbe di alcuni piuttosto brutto (e lento) query XPath utilizzando il asse following-sibling
con filtri sull'asse preceding-sibling
corrispondenti al nodo corrente.
Se è affatto una possibilità, mi sento di raccomandare la creazione della gerarchia al di fuori di XSLT (in C #, Java, ecc)
Se si sceglie di andare giù per il sentiero spaventoso, si dovrebbe essere in cerca di fare qualcosa di simile (non testata):
<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>
...
Questo è solo un esempio, e posso già vedere problemi con la partita. Venendo con la query XPath finale sarebbe del tutto coinvolto, se in realtà è possibile a tutti.
Se non è possibile utilizzare XSLT 2.0, qui è un foglio di stile XSLT 1.0 che dovrebbe produrre lo stesso risultato come il foglio di stile XSLT 2.0 che ho postato in precedenza:
<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>