Reputation: 21
Given the following XML:
<root>
<group>
<e1>001</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
<group>
<e1>002</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
<group>
<e1>003</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
<group>
<e1>004</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
</root>
Note that the 'e2' elements within each 'group' element are all the same, and this is guaranteed in the source document.
I am trying to use XSLT to do the following steps:
The desired output would look like this:
<root>
<group>
<e1>default1</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
<group>
<e1>default2</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
</root>
The values of 'e1' in the source document are irrelevant, and the values of 'e2' in the output document are known ahead of time and static. It's only the 'e2' values that are dynamic and I need to make sure that they are all there.
I've used a pattern similar to this before when replacing all elements with some hard-coded values before hand:
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- Empty Template eliminates all but first 'group' element. -->
<xsl:template match="//group[preceding::group]"></xsl:template>
<xsl:template match="//group">
<xsl:element name="group">
<e1>default1</e1>
<!-- e2 elements inserted here somehow? -->
</xsl:element>
<xsl:element name="group">
<e1>default2</e1>
<!-- e2 elements inserted here somehow? -->
</xsl:element>
</xsl:template>
I tried storing those elements to a variable, but nothing was inserted into output html:
<xsl:variable name="e2Elements" select="//group[1]/e2"></xsl:variable>
<xsl:template match="//group">
<xsl:element name="group">
<e1>default1</e1>
<xsl:copy-of select="$e2Elements" />
</xsl:element>
</xsl:template>
But am not sure how to get the e2 elements inserted into the values. I'm using SaxonHE9.8N and have access to exslt namespace and xslt2.0
Upvotes: 0
Views: 118
Reputation: 163458
Your solution is actually making an unnecessary copy of the elements. You can do it without copying them, like this:
<xsl:variable name="e3Elements" select="//group[1]/e2" />
Another inefficiency is the <xsl:template match="group[preceding::group]"/>
Using the preceding axis is always expensive, but especially so in a pattern. The obvious improvement is to replace it with preceding-sibling (searching the preceding-sibling axis is much faster than searching the preceding axis). But in fact you can do better than this: make this the default rule for groups (<xsl:template match="group"/>
), and make the other rule only match the first group (<xsl:template match="group[1]">...
).
But actually, there's no need to match the first group element either, because you're not using any of its data.
On a stylistic point, <group>
is to be preferred over <xsl:element name="group">
simply because it's much more readable.
This would be my XSLT 3.0 solution:
<xsl:transform version="3.0" .... expand-text="yes">
<xsl:template match="/">
<xsl:variable name="e3Elements" select="//group[1]/e2"/>
<xsl:for-each select="'default1', 'default2'">
<group>
<e1>{.}</e1>
<xsl:copy-of select="$e3Elements"/>
</group>
</xsl:for-each>
</xsl:template>
</xsl:transform>
Upvotes: 2
Reputation: 21
Wound up that I needed to make my variable a copy of the element using copy-of. Below is my solution:
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="e3Elements">
<xsl:copy-of select="//group[1]/e2" />
</xsl:variable>
<xsl:template match="group[preceding::group]"></xsl:template>
<xsl:template match="group">
<xsl:element name="group">
<xsl:element name="e1">default1</xsl:element>
<xsl:copy-of select="$e3Elements"></xsl:copy-of>
</xsl:element>
<xsl:element name="group">
<xsl:element name="e1">default2</xsl:element>
<xsl:copy-of select="$e3Elements"></xsl:copy-of>
</xsl:element>
</xsl:template>
Upvotes: 1