简体   繁体   English

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

[英]How to remove the first child of an XML node with XSL?

Data.xls 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>

How to remove the first child in Data and produce output that could be passed to remove-first-child again? 如何删除数据中的第一个孩子并产生可以再次传递给remove-first-child的输出?

When calling the first template I get: 调用第一个模板时,我得到:

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

When I change to call the second template I get: 当我更改为调用第二个模板时,我得到:

$> 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.

The desired result would be: 理想的结果将是:

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

Update: How I tried to apply the suggested solutions. 更新:我如何尝试应用建议的解决方案。

<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>

But this also only yields the same results as mentioned above. 但这也只会产生与上述相同的结果。 a : a

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

b : b

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

So I guess the key is the type system... 所以我想关键是类型系统...

And as a general note: The main manipulation is happening for Other.xml , it's not just a dummy call. 并且要注意:主要操作发生在Other.xml ,这不仅仅是一个虚拟调用。 Some data is taken from Data.xml and it's to be processed before joined with Other.xsl . 一些数据是从Data.xml的,在与Other.xsl之前将要进行处理。 That processing consists only of the removal of the first child dependent on some conditions (not part of this question) which happen to be implemented recursively, because it's XSL. 该处理仅包括删除依赖于某些条件(不是此问题的一部分)的第一个子对象,这些条件恰好是递归实现的,因为它是XSL。 And along the recursions it might be necessary to remove several first children of the same and of different, traversed nodes. 并且在递归过程中,可能有必要删除相同和不同的遍历节点的多个第一个子级。

Whata I'm trying to express in XSL here is a projection node --> node , removing the first child of the input node. 我想在XSL中表示的是投影node --> node ,删除输入节点的第一个子节点。

For this kind of recursion in XSLT (ie, following the input document's structure), you want to use apply-templates , not call-template . 对于XSLT中的这种递归(即,遵循输入文档的结构),您要使用apply-templates而不是call-template This avoids the issue you're getting (missing node set), since the template will only be applied to nodes that exist. 这避免了您遇到的问题(缺少节点集),因为该模板将仅应用于存在的节点。

The following transform sets up a standard identity template, then adds an override that emits nothing for first children. 以下转换设置了一个标准的身份模板,然后添加了对第一个子对象不产生任何影响的替代。

<?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>

Applied on a dummy document where the external file Data.xml contains the following sample input, 应用于虚拟文档,其中外部文件Data.xml包含以下示例输入,

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

This gives the result: 结果如下:

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

You can use : 您可以使用 :

<?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>

And change the 1 by 2 if you want to filter both the first and second element (it works as explained in harpo's answer). 如果要同时过滤第一个和第二个元素,则将“ 1”乘“ 2”(按harpo的答案中的说明工作)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM