我想根据以下规则将两个文件A.xml和map.xml与“Node”元素合并(节点由@Name区分):
示例:
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>
结果应为:
<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>
我的XSLT脚本是:
<?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>
脚本运行正常。它处理A.xml并在map.xml中查找每个Node元素。由于可以混合使用@Src和非@ Src节点,因此可以递归调用它。
但是,此脚本会生成:
<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>
所以,Child4_Sub2生成两次是没有意义的,因为Child4_Sub2无论如何都需要Child4作为父级!但到目前为止,我发现没有办法阻止这个元素被打印出来。
你有任何提示吗?
此致 DIVB
答案 0 :(得分:5)
变化:
<xsl:apply-templates mode="MapDiagram" select=
"$MapDiagram//Node[@Name = $MyName]"/>
为:
<xsl:if test="not(@Name = ancestor::Node/@Name)">
<xsl:apply-templates mode="MapDiagram" select=
"$MapDiagram//Node[@Name = $MyName]"/>
</xsl:if>
这是一个完整的解决方案:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="mapFile"
select="'file:///c:/temp/delete/map.xml'"/>
<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:if test="not(@Name = ancestor::Node/@Name)">
<xsl:apply-templates mode="MapDiagram" select=
"$MapDiagram//Node[@Name = $MyName]"
/>
</xsl:if>
</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>
将此转换应用于提供的XML文档:
<Node Name="ParentNode">
<Node Name="Child4">
<Node Name="Child4">
<Node Name="Child4_Sub1"/>
</Node>
</Node>
<Node Name="Child1"/>
<Node Name="ChildFoo"/>
</Node>
,提供的“map.xml”为at C:\temp\delete\map.xml
:
<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>
生成想要的结果(不包含不需要的重复):
<Node Name="ParentNode" Type="">
<Node Name="Child4" Type="">
<Node Name="Child4" Type="">
<Node Name="Child4_Sub1" Type=""/>
</Node>
<Node Name="Child4_Sub2" Src="Child4_Sub2/"/>
</Node>
<Node Name="Child1" Src="Child1/"/>
<Node Name="Child2" Src="Child2/"/>
<Node Name="Child3" Src="Child3/"/>
<Node Name="Child4_Sub2" Src="Child4_Sub2/"/>
<Node Name="Child5"/>
</Node>
一般说明:提供的代码非常复杂和混乱 - 可能还有其他逻辑问题。没有使用XSLT 2.0语言功能 - 这本质上是一个XSLT 1.0解决方案。以更好的形式重写代码是个好主意。
答案 1 :(得分:3)
无法直接检查结果文档以查看是否已发出给定节点。您可以通过将节点输出到临时变量(可以检查)来解决这个问题,然后稍后输出该变量的内容。
但是,在生成变量节点后,不可能修改变量节点的内部结构,因此普通迭代会很难。您只能通过修改内容来复制内容。这看起来有点矫枉过正,但如果文件不是很大,那将是一种选择。
通常情况下,我们不是通过检查输出来解决这个问题,而是通过检查已经处理过的输入,或者针对保持累积结果的变量来解决。 (注意,要在变量中累积结果,必须递归传递变量。)
答案 2 :(得分:0)
此外,您可以在XSL内部使用VBScript / JScript,以便使您无法使用XSL本身。您可以创建VBS函数来检查Row是否存在并返回True或False。 XSL将检查函数结果,如果为false,将跳过元素生成并进一步移动。当然,您需要在Script中管理添加的行。