Background
A more general version of my problem was recently posted but due to the quality of the post, it received no responses. Here's my attempt!
I have the following XML structure that I want to transform
Pre-processed XML
<schema>
<!-- element to be imbedded -->
<element name="tryCatch.catch">
<complexType>
<complexContent>
<extension base="escalateContainer">
<attribute name="var" type="_var" />
</extension>
</complexContent>
</complexType>
</element>
<complexType name="tryCatch">
<complexContent>
<extension base="scriptElement">
<sequence>
<element name="try" type="escalateContainer" />
<element name="catch" type="tryCatch.catch" minOccurs="0" />
<element name="finally" type="escalateContainer" minOccurs="0" />
</sequence>
</extension>
</complexContent>
</complexType>
</schema>
...into the following structure:
Post XSLT-Structure
<schema>
<complexType name="tryCatch">
<complexContent>
<extension base="scriptElement">
<sequence>
<element name="try" type="escalateContainer" />
<!-- Change here! -->
<element name="tryCatch.catch" minOccurs="0">
<complexType>
<complexContent>
<extension base="escalateContainer">
<attribute name="var" type="_var" />
</extension>
</complexContent>
</complexType>
</element>
<element name="finally" type="escalateContainer" minOccurs="0" />
</sequence>
</extension>
</complexContent>
</complexType>
</schema>
Overview of the Process
Basically, I just want to
- Copy everything
Then find all
element
children of acomplexType
that have a.
in their type (by construction - I have ensured this pattern works interally in my XML). A simplified version of my match is:complexType.element.type = element.name
Last, replace that element with the corresponding global element. Bonus points if the attributes on the element being replaced are also copied.
What I have So Far
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="schema">
<xsl:for-each select="element">
<xsl:variable name="self" select="current()" />
<xsl:variable name="name" select="@name" />
<xsl:for-each select="/schema/complexType//element[@type=$name]">
<xsl:copy-of select="$self" />
</xsl:for-each>
</xsl:for-each>
</xsl:template>
I think I'm off to the right start - copy everything with the identity template, then process what I want to change. The problem is that my second template only returns the copied element! The match appears to work great, I just lose all the other content I'm trying to preserve.
Generally the usage of
for-each
isn't the best idea. It makes the templates inflexible and hard to read. (Of course sometimes you can't avoid it but in this case it is possible.) You should leave iteration to the XSLT engine by usingapply-templates
. I'd do something like this:Update: I've just realised I got
@type
and@name
mixed up, fixed that.