如何使用XSL删除XML节点的第一个子节点?

时间:2014-05-07 15:05:21

标签: xml xslt

Data.xls

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:variable name="Data" select="document('Data.xml')/*[1]"/>

<xsl:template match="/">
  <xsl:call-template name="remove-first-child">
    <xsl:with-param name="Node" select="$Data"/>
  </xsl:call-template>
</xsl:template>

<xsl:template name="remove-first-child">
  <xsl:param name="Node"/>
  <xsl:element name="{name($Node)}">
    <xsl:copy-of select="$Node/@* | $Node/*[position() > 1]"/>
  </xsl:element>
</xsl:template>

<!-- same as above, but for testing over 2 recursion levels -->
<xsl:template name="remove-first-child1">
  <xsl:param name="Node"/>
  <xsl:call-template name="remove-first-child">
    <xsl:with-param name="Node">
      <xsl:element name="{name($Node)}">
        <xsl:copy-of select="$Node/@* | $Node/*[position() > 1]"/>
      </xsl:element>
    </xsl:with-param>
  </xsl:call-template>
</xsl:template>

</xsl:stylesheet>

如何删除数据中的第一个子节点并生成可以再次传递给remove-first-child的输出?

调用我得到的第一个模板时:

$> msxsl Other.xml Data.xsl
<Alphabet>
  <B/>
  <C/>
  ...
</Alphabet>

当我改为调用第二个模板时,我得到了:

$> msxsl Other.xml Data.xsl
Error occured while executing stylesheet 'Data.xsl'.
Code:  0x80004005
Reference to variable or parameter 'Node' must evaluate to a node list.

期望的结果是:

<Alphabet>
  <C/>
  ...
</Alphabet>

更新:我如何尝试应用建议的解决方案。

<xsl:template match="node()|@*" mode="remove-first-child">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*" mode="remove-first-child"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="node()[parent::*][not(preceding-sibling::*)]" mode="remove-first-child"/>

<xsl:template name="remove-first-child-a">
  <xsl:param name="Node"/>
  <xsl:apply-templates select="$Node" mode="remove-first-child"/>
</xsl:template>

<xsl:template name="remove-first-child-b">
  <xsl:param name="Node"/>
  <xsl:variable name="Temp">
    <xsl:apply-templates select="$Node" mode="remove-first-child"/>
  </xsl:variable>
  <xsl:apply-templates select="$Temp" mode="remove-first-child"/>  
</xsl:template>

<xsl:template match="/">
  <xsl:call-template name="remove-first-child-a">
  <!-- xsl:call-template name="remove-first-child-b" -->
    <xsl:with-param name="Node" select="$Data"/>
  </xsl:call-template>
</xsl:template>

但这也只能产生与上述相同的结果。 a

$> msxsl Other.xml Data.xsl
<Alphabet>
  <B/>
  <C/>
  ...
</Alphabet>

b

$> msxsl Other.xml Data.xsl
Error occured while executing stylesheet 'Data.xsl'.
Code:  0x80004005
Expression must evaluate to a node-set.
-->$Temp<--

所以我猜关键是类型系统......

并且作为一般性说明:Other.xml正在进行主要操作,它不仅仅是一个虚拟调用。有些数据来自Data.xml,需要在加入Other.xsl之前进行处理。该处理仅包括根据某些条件(不是这个问题的一部分)删除第一个孩子,这些条件恰好是递归实现的,因为它是XSL。在递归过程中,可能需要删除相同和不同的遍历节点的几个第一个子节点。

我试图在XSL中表达一个投影node --> node,删除输入节点的第一个子节点。

2 个答案:

答案 0 :(得分:1)

对于XSLT中的这种递归(即,遵循输入文档的结构),您希望使用apply-templates,而不是call-template。这样可以避免您遇到的问题(缺少节点集),因为模板只会应用于存在的节点。

以下转换设置标准身份模板,然后添加一个不为第一个孩子发送任何内容的覆盖。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <!-- Standard identity template -->
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>

    <!-- Skip first child -->
    <!-- the "parent" check is needed so that the root node is not skipped -->
    <xsl:template match="node()[parent::*][not(preceding-sibling::*)]" />

    <xsl:variable name="data-doc" select="document('Data.xml')"/>

    <xsl:template match="/">
        <xsl:apply-templates select="$data-doc/*" />
    </xsl:template>

</xsl:stylesheet>

应用于虚拟文档,其中外部文件Data.xml包含以下示例输入

<A>
    <B1>
        <C1 />
        <C2 />
    </B1>
    <B2>
        <C1 />
        <C2 />
    </B2>
    <B3>
        <C1 />
        <C2>
            <D1 />
            <D2 />
        </C2>
        <C3>
            <D1 />
        </C3>
    </B3>
</A>

这给出了结果:

<?xml version="1.0" encoding="utf-8"?>
<A>
    <B2>
        <C2 />
    </B2>
    <B3>
        <C2>
            <D2 />
        </C2>
        <C3>
        </C3>
    </B3>
</A>

答案 1 :(得分:0)

您可以使用:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/*//*[position()&lt;=1]"/>     
</xsl:stylesheet>

如果你想要过滤第一个和第二个元素,它会将1改为2(它的工作方式与harpo的答案相同)。