每个XSL参数按名称删除元素和/或属性

Wit*_*man 13 xml xslt xslt-2.0 xslt-1.0

以下是通过名称(在此示例中为"removeMe")从XML文件中删除不需要的元素和属性的工作:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match="removeMe"/>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

问题是它没有区分元素和属性,名称是硬编码的,它只能采用一个名称.如何重写以使用下面的几个输入参数来删除一个或多个特定元素和/或属性?

<xsl:param name="removeElementsNamed"/>
<xsl:param name="removeAttributesNamed"/>
Run Code Online (Sandbox Code Playgroud)

期望的结果是能够移除 一个或多个 元素和/或 一个或多个 属性,同时仍然区分元素和属性(换句话说,应该可以删除所有"时间" 元素 而不删除所有"时间" 属性).

虽然我在本轮中需要XSLT 1.0,但在接受和其他答案中的XSLT 2.0解决方案可能对其他人有用.

Dim*_*hev 22

这种转变:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="removeElementsNamed" select="'x'"/>

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

 <xsl:template match="*">
  <xsl:if test="not(name() = $removeElementsNamed)">
   <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当应用于任何XML文档时,请说:

<t>
    <a>
        <b/>
        <x/>
    </a>
    <c/>
    <x/>
    <d/>
</t>
Run Code Online (Sandbox Code Playgroud)

生成所需的正确结果 - 源XML文档的副本,其中$removeElementsNamed删除任何名称为参数值的元素的出现:

<t>
   <a>
      <b/>
   </a>
   <c/>
   <d/>
</t>
Run Code Online (Sandbox Code Playgroud)

请注意:在XSLT 1.0中,在模板匹配模式中包含变量或参数引用在语法上是非法的.这就是为什么@JanThomä和@treeMonkey的解决方案都会引起任何符合XSLT 1.0标准的处理器的错误.

更新:这是一个更复杂的解决方案,允许将管道分隔的元素名称列表 - 删除,传递给转换:

<xsl:stylesheet version="1.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="removeElementsNamed" select="'|x|c|'"/>

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

 <xsl:template match="*">
  <xsl:if test=
   "not(contains($removeElementsNamed,
                 concat('|',name(),'|' )
                 )
        )
   ">
   <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当应用于同一XML文档(上面)时,转换再次生成所需的正确输出 - 源XML文档以及其名称在$removeElementsNamed参数中指定的所有元素- 已删除:

<t>
   <a>
      <b/>
   </a>
   <d/>
</t>
Run Code Online (Sandbox Code Playgroud)

Update2:与Update1中的转换相同,但是使用XSLT 2.0编写:

<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="removeElementsNamed" select="'|x|c|'"/>

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

 <xsl:template match=
 "*[name() = tokenize($removeElementsNamed, '\|')]"/>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

更新:OP已添加要求,也可以删除具有某些特定名称的所有属性.

以下是适应这一新要求的略微修改的转换:

<xsl:stylesheet version="1.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="removeElementsNamed" select="'x'"/>
     <xsl:param name="removeAttributesNamed" select="'n'"/>

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

     <xsl:template match="*">
      <xsl:if test="not(name() = $removeElementsNamed)">
       <xsl:call-template name="identity"/>
      </xsl:if>
     </xsl:template>

     <xsl:template match="@*">
      <xsl:if test="not(name() = $removeAttributesNamed)">
       <xsl:call-template name="identity"/>
      </xsl:if>
     </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当这个转换应用于下面的XML文档时(之前使用的那个,但添加了一些属性):

<t>
    <a>
        <b m="1" n="2"/>
        <x/>
    </a>
    <c/>
    <x/>
    <d n="3"/>
</t>
Run Code Online (Sandbox Code Playgroud)

生成所需的正确结果(命名的所有元素x和命名的所有属性n都被删除):

<t>
   <a>
      <b m="1"/>
   </a>
   <c/>
   <d/>
</t>
Run Code Online (Sandbox Code Playgroud)

UPDATE2:再次请求OP,我们现在实现了传递管道分隔的名称列表的功能,以删除具有这些名称的元素,并分别使用管道分隔的名称列表来删除具有以下名称的属性:

<xsl:stylesheet version="1.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="removeElementsNamed" select="'|c|x|'"/>
     <xsl:param name="removeAttributesNamed" select="'|n|p|'"/>

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

     <xsl:template match="*">
      <xsl:if test=
      "not(contains($removeElementsNamed,
                    concat('|', name(), '|')
                    )
           )
      ">
       <xsl:call-template name="identity"/>
      </xsl:if>
     </xsl:template>

     <xsl:template match="@*">
      <xsl:if test=
      "not(contains($removeAttributesNamed,
                    concat('|', name(), '|')
                    )
           )
       ">
       <xsl:call-template name="identity"/>
      </xsl:if>
     </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

将此转换应用于以下XML文档时:

<t>
    <a p="0">
        <b m="1" n="2"/>
        <x/>
    </a>
    <c/>
    <x/>
    <d n="3"/>
</t>
Run Code Online (Sandbox Code Playgroud)

的想要的,正确的结果产生(其名称元素cx并用名字属性np被删除):

<t>
   <a>
      <b m="1"/>
   </a>
   <d/>
</t>
Run Code Online (Sandbox Code Playgroud)