I want to merge two files A.xml and map.xml with “Node” elements according to the following rule (nodes are distinguished by @Name):
- If the element in map.xml has a Src attribute, the element from map should be copied to output
- If the element exists in A and map and does NOT have @Src, it should be copied from A
- If the element exists in A but not in map, it should be ignored (with warning)
- If the element exists in map but not in A, the (empty) element shall be generated
Example:
map.xml:
<?xml version="1.0"?>
<Node Name="ParentNode">
<Node Name="Child1" Src="Child1/"/>
<Node Name="Child2" Src="Child2/"/>
<Node Name="Child3" Src="Child3/"/>
<Node Name="Child4">
<Node Name="Child4_Sub1" />
<Node Name="Child4_Sub2" Src="Child4_Sub2/"/>
</Node>
<Node Name="Child5" />
</Node>
A.xml:
<Node Name="ParentNode">
<Node Name="Child4">
<Node Name="Child4">
<Node Name="Child4_Sub1">
<!-- Here are many other elements -->
</Node>
</Node>
</Node>
<!-- Here are many other elements -->
<Node Name="Child1">
<!-- Here are many other elements -->
</Node>
<!-- Here are many other elements -->
<Node Name="ChildFoo">
<!-- Here are many other elements -->
</Node>
</Node>
The result should be:
<Node Name="ParentNode">
<Node Name="Child4">
<Node Name="Child4">
<Node Name="Child4_Sub1">
<!-- Here are many other elements -->
</Node>
<Node Name="Child4_Sub2" />
</Node>
</Node>
<!-- Here are many other elements -->
<Node Name="Child1" Src="Child1" />
<!-- Here are many other elements -->
<Node Name="Child2" Src="Child2" />
<Node Name="Child3" Src="Child3" />
</Node>
My XSLT script is:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0">
<xsl:param name="mapFile" required="yes"/>
<xsl:variable name="MapDiagram" select="document($mapFile,/*)"/>
<xsl:variable name="CurrentDocument" select="/" />
<!-- handle Node elements in A.xml -->
<xsl:template match="Node">
<xsl:variable name="MyName" select="@Name"/>
<xsl:choose>
<xsl:when test="$MapDiagram//Node[@Name = $MyName]">
<xsl:choose>
<xsl:when test="$MapDiagram//Node[@Name = $MyName]/@Src">
<xsl:copy-of select="$MapDiagram//Node[@Name = $MyName]"/>
</xsl:when>
<xsl:otherwise>
<Node Name="{@Name}" Type="{@Type}">
<xsl:apply-templates/>
<xsl:apply-templates select="$MapDiagram//Node[@Name = $MyName]" mode="MapDiagram" />
</Node>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="no">WARNING: Node "<xsl:value-of select="@Name"/>" not found in map file, ignoring</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- handle Node elements from map file -->
<xsl:template match="Node" mode="MapDiagram">
<xsl:variable name="MyName" select="@Name"/>
<xsl:choose>
<xsl:when test="not($CurrentDocument//Node[@Name = $MyName])">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates mode="MapDiagram" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Copy all other elements in between -->
<xsl:template match="*[name() != 'Node']">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
The script works fine. It processes A.xml and looks up each Node element in map.xml. Since @Src and non-@Src Nodes can be mixed, it is called recursively.
However, this script generates:
<Node Name="ParentNode">
<Node Name="Child4">
<Node Name="Child4">
<Node Name="Child4_Sub1">
<!-- Here are many other elements -->
</Node>
<Node Name="Child4_Sub2" />
</Node>
</Node>
<!-- Here are many other elements -->
<Node Name="Child1" Src="Child1" />
<!-- Here are many other elements -->
<Node Name="Child2" Src="Child2" />
<Node Name="Child3" Src="Child3" />
<Node Name="Child4_Sub2" />
</Node>
So, the Child4_Sub2 is generated twice which is non-sense since Child4_Sub2 would need Child4 as parent anyway! But so far I found no way to prevent this element from being printed.
Do you have any hints?
Regards,
divB
Change:
to:
Here is a complete solution:
When this transformation is applied to the provided XML document:
and the provided “map.xml” is
at C:\temp\delete\map.xml:The wanted result (not containing the unwanted repetition) is produced:
General note: The code that is provided is quite complicated and messy — there might be other logical issues with it. No XSLT 2.0 language features are used — this is essentially an XSLT 1.0 solution. It would be a good idea to rewrite your code in a better form.