检查* output *节点是否已存在

时间:2012-01-26 08:20:09

标签: xml xslt

我想根据以下规则将两个文件A.xml和map.xml与“Node”元素合并(节点由@Name区分):

  1. 如果map.xml中的元素具有Src属性,则应将map中的元素复制到输出
  2. 如果元素存在于A和map中且没有@Src,则应从A
  3. 复制
  4. 如果元素存在于A但不存在于地图中,则应忽略该元素(带警告)
  5. 如果元素存在于map中而不​​存在于A中,则应生成(empty)元素
  6. 示例:

    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

3 个答案:

答案 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中管理添加的行。

VBScript exampleJS Example