按每个 XSL 参数的名称删除元素和/或属性 [英] Remove Elements and/or Attributes by Name per XSL Parameters

查看:28
本文介绍了按每个 XSL 参数的名称删除元素和/或属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下内容按名称(在本例中为removeMe")从 XML 文件中删除不需要的元素和属性:

The following does the job of removing unwanted elements and attributes by name ("removeMe" in this example) from an XML file:

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

问题是不区分元素和属性,名字是硬编码的,只能取一个名字.如何将其重写为使用如下几个输入参数来删除一个或多个特定元素和/或属性?

The problems are it does not distinguish between elements and attributes, the name is hard-coded, and it can only take one name. How could this be rewritten to use a couple input parameters like below to remove one or more specific elements and/or attributes?

<xsl:param name="removeElementsNamed"/>
<xsl:param name="removeAttributesNamed"/>

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

The desired result is the ability to remove one or more elements and/or one or more attributes while still distinguishing between elements and attributes (in other words, it should be possible to remove all "time" elements without also removing all "time" attributes).

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

While I required XSLT 1.0 this round, XSLT 2.0 solutions in accepted and other answers may be useful to others.

推荐答案

这种转变:

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

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

<t>
    <a>
        <b/>
        <x/>
    </a>
    <c/>
    <x/>
    <d/>
</t>

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

produces the wanted correct result -- a copy of the source XML document in which any occurence of element having the name that is the value of the $removeElementsNamed parameter, is deleted:

<t>
   <a>
      <b/>
   </a>
   <c/>
   <d/>
</t>

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

Do note: In XSLT 1.0 it is syntactically illegal to have a variable or parameter reference inside a template match pattern. This is why the solutions by @Jan Thomä and @treeMonkey both raise an error with any XSLT 1.0 - compliant processor.

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

Update: Here is a more complicated solution, that allows a pipe-separated list of element names - to be deleted, to be passed to the transformation:

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

当应用于同一个 XML 文档(上图)时,转换再次产生所需的正确输出——源 XML 文档,其名称在 $removeElementsNamed 中指定参数 -- 删除:

When applied to the same XML document (above), the transformation produces again the wanted, correct output -- the source XML document with all elements whose name are specified in the $removeElementsNamed parameter -- deleted:

<t>
   <a>
      <b/>
   </a>
   <d/>
</t>

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

Update2: The same transformation as in Update1, but written in 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>

更新:OP 已添加要求还能够删除具有特定名称的所有属性.

Update: The OP has added the requirement to also be able to delete all attributes that have some specific name.

为了适应这个新的需求,下面是稍微修改的转换:

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

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

When this transformation is applied on the XML document below (the one used before but with a few attributes added):

<t>
    <a>
        <b m="1" n="2"/>
        <x/>
    </a>
    <c/>
    <x/>
    <d n="3"/>
</t>

生成想要的正确结果(删除所有名为 x 的元素和名为 n 的所有属性):

the wanted, correct result is produced (all elements named x and all attributes named n are deleted):

<t>
   <a>
      <b m="1"/>
   </a>
   <c/>
   <d/>
</t>

UPDATE2:正如 OP 再次要求的那样,我们现在实现了传递管道分隔名称列表以删除具有这些名称的元素的功能,以及管道分隔的名称列表删除具有这些名称的属性:

UPDATE2: As again requested by the OP, we now implement the capability to pass pipe-separated list of names for the deletion of elements with these names and respectively a pipe-separated list of names for the deletion of attributes with these names:

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

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

<t>
    <a p="0">
        <b m="1" n="2"/>
        <x/>
    </a>
    <c/>
    <x/>
    <d n="3"/>
</t>

产生想要的、正确的结果(名称为 cx 的元素以及名称为 n 的属性和 p 被删除):

the wanted, correct result is produced (elements with names c and x and attributes with names n and p are deleted):

<t>
   <a>
      <b m="1"/>
   </a>
   <d/>
</t>

这篇关于按每个 XSL 参数的名称删除元素和/或属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆