Reputation: 405
I need to format XML input using XSL to obtain more convenient structure. As a next step of processing i want to transform it to HTML. Suppose i have the following input: (0)
<list>
<item item-id="1" second-item-id="1" third-item-id="1"/>
<item item-id="1" second-item-id="1" third-item-id="2"/>
<item item-id="1" second-item-id="2" third-item-id="1"/>
<item item-id="1" second-item-id="3" third-item-id="1"/>
<item item-id="2" second-item-id="1" third-item-id="1"/>
<item item-id="2" second-item-id="1" third-item-id="2"/>
<item item-id="2" second-item-id="1" third-item-id="3"/>
<item item-id="2" second-item-id="2" third-item-id="1"/>
<item item-id="3" second-item-id="1" third-item-id="1"/>
<item item-id="3" second-item-id="1" third-item-id="2"/>
<item item-id="3" second-item-id="1" third-item-id="3"/>
<item item-id="3" second-item-id="1" third-item-id="4"/>
</list>
and the following XSL template: (1)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:key name="itemKey" match="item" use="@item-id"/>
<xsl:key name="secondItemKey" match="item" use="concat(@item-id, '|', @second-item-id)"/>
<xsl:template match="list">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="item[generate-id() = generate-id(key('itemKey', @item-id)[1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<item item-id="{@item-id}">
<xsl:apply-templates select="key('itemKey', @item-id)[generate-id() = generate-id(key('secondItemKey', concat(@item-id, '|', @second-item-id))[1])]" mode="evt"/>
</item>
</xsl:template>
<xsl:template match="item" mode="evt">
<second-item second-item-id="{@second-item-id}">
<xsl:apply-templates select="key('secondItemKey', concat(@item-id, '|', @second-item-id))" mode="bus"/>
</second-item>
</xsl:template>
<xsl:template match="item" mode="bus">
<third-item third-item-id="{@third-item-id}"/>
</xsl:template>
</xsl:stylesheet>
it gives me pretty fine XML: (2)
<?xml version="1.0"?>
<list>
<item item-id="1">
<second-item second-item-id="1">
<third-item third-item-id="1"/>
<third-item third-item-id="2"/>
</second-item>
<second-item second-item-id="2">
<third-item third-item-id="1"/>
</second-item>
<second-item second-item-id="3">
<third-item third-item-id="1"/>
</second-item>
</item>
<item item-id="2">
<second-item second-item-id="1">
<third-item third-item-id="1"/>
<third-item third-item-id="2"/>
<third-item third-item-id="3"/>
</second-item>
<second-item second-item-id="2">
<third-item third-item-id="1"/>
</second-item>
</item>
<item item-id="3">
<second-item second-item-id="1">
<third-item third-item-id="1"/>
<third-item third-item-id="2"/>
<third-item third-item-id="3"/>
<third-item third-item-id="4"/>
</second-item>
</item>
</list>
i have another XSL which transforms XML #2 to html:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="html"/>
<xsl:template match="list">
<xsl:for-each select="item">
<h2><xsl:value-of select="concat(local-name(),' ',@item-id)"/></h2>
<ul>
<xsl:for-each select="second-item">
<li><xsl:value-of select="concat(local-name(),' ',@second-item-id)"/></li>
<ul>
<xsl:for-each select="third-item">
<li><xsl:value-of select="concat(local-name(),' ',@third-item-id)"/></li>
</xsl:for-each>
</ul>
</xsl:for-each>
</ul>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
So here is the question: I want to process input xml with both of tempaltes (or merged one) in one step. How can i do it?
Thanks in advance.
Upvotes: 1
Views: 645
Reputation: 101680
If you're happy with just merging them together, then this should accomplish the job of both at the same time:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:key name="itemKey" match="item" use="@item-id"/>
<xsl:key name="secondItemKey" match="item" use="concat(@item-id, '|', @second-item-id)"/>
<xsl:template match="list">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="item[generate-id() = generate-id(key('itemKey', @item-id)[1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<h2>
<xsl:value-of select="concat('item ', @item-id)"/>
</h2>
<ul>
<xsl:apply-templates select="key('itemKey', @item-id)[generate-id() = generate-id(key('secondItemKey', concat(@item-id, '|', @second-item-id))[1])]" mode="evt"/>
</ul>
</xsl:template>
<xsl:template match="item" mode="evt">
<li>
<xsl:value-of select="concat('second-item ', @second-item-id)"/>
</li>
<ul>
<xsl:apply-templates select="key('secondItemKey', concat(@item-id, '|', @second-item-id))" mode="bus"/>
</ul>
</xsl:template>
<xsl:template match="item" mode="bus">
<li>
<xsl:value-of select="concat('third-item ', @third-item-id)"/>
</li>
</xsl:template>
</xsl:stylesheet>
There is an easy enough way to have them both in one XSLT and run one after the other, but the approach I have in mind would require the use of the node-set()
function which is unfortunately in a different namespace for each XSLT implementation. Which XSLT processor are you using?
Upvotes: 1