Reputation: 89
I am completely new to xslt. Please help me to write style sheet.I have input xml like this
Input XML:
<elements>
<e1>
<pid>1</pid>
<cid>2</cid>
</e1>
<e1>
<pid>1</pid>
<cid>3</cid>
</e1>
<e1>
<pid>2</pid>
<cid>4</cid>
</e1>
</elements>
Desired XML:
<tree>
<unit id="1">
<unit id="2">
<unit id="4">
<data></data>
</unit>
<data></data>
</unit>
<unit id="3">
<data></data>
</unit>
<data></data>
</unit>
</tree>
I feel this should be really easy but I'm struggling to find information about how to do this. My XSLT knowledge isn't great.
Upvotes: 1
Views: 1527
Reputation: 163458
Ah, after reading JLRishe I think I get it: "pid" means "parent ID", "cid" means "child ID", and e1 represents a parent-child relationship. Brilliant detective work, I would never have worked that out for myself.
The basic model is that when you are positioned on a parent element you do apply-templates to its children. This applies just as well if the parent/child relationships are represented by primary/foreign keys as when they are represented using the XML hierarchy. So the essence is:
<xsl:template match="e1">
<unit id="{pid}">
<xsl:apply-templates select="//e1[pid=current()/cid]"/>
<data/>
</unit>
</xsl:template>
which is essentially JLRishe's solution except he has added an optimization using keys.
Upvotes: 0
Reputation: 101730
I'm not 100% sure how you want the XSLT to determine from that input that the top id is 1 (is it because it's the only pid
value with no corresponding cid
values, or is it always 1?). Nonetheless, this should do the job:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kItemsByC" match="e1" use="cid" />
<xsl:key name="kItemsByP" match="e1" use="pid" />
<xsl:template match="/">
<tree>
<xsl:call-template name="Unit">
<!-- This will be the value of the <pid> that has no <cid> references to
it (assuming there is only one top-level <pid>) -->
<xsl:with-param name="id"
select="string(/elements/e1/pid[not(key('kItemsByC', .))])" />
</xsl:call-template>
</tree>
</xsl:template>
<xsl:template match="e1" name="Unit">
<xsl:param name="id" select="cid" />
<unit id="{$id}">
<xsl:apply-templates select="key('kItemsByP', $id)" />
<data />
</unit>
</xsl:template>
</xsl:stylesheet>
When this is run on your sample input, this produces:
<tree>
<unit id="1">
<unit id="2">
<unit id="4">
<data />
</unit>
<data />
</unit>
<unit id="3">
<data />
</unit>
<data />
</unit>
</tree>
Note: The above XSLT has logic to attempt to dynamically locate the top-level ID. If it can be assumed that the top-level unit will always have ID 1, then one key and the above XSLT's (somewhat) complicated formula can be eliminated:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kItemsByP" match="e1" use="pid" />
<xsl:template match="/">
<tree>
<xsl:call-template name="Unit">
<xsl:with-param name="id" select="1" />
</xsl:call-template>
</tree>
</xsl:template>
<xsl:template match="e1" name="Unit">
<xsl:param name="id" select="cid" />
<unit id="{$id}">
<xsl:apply-templates select="key('kItemsByP', $id)" />
<data />
</unit>
</xsl:template>
</xsl:stylesheet>
This also produces the requested output when run on your sample input.
Upvotes: 2