如何使用XSLT从XML文件中删除不需要的元素和属性

4
我有一个 XML 文件,想要完整复制它,但是我想过滤掉一些不需要的元素和属性,例如以下是原始文件:
<root>
<e1 att="test1" att2="test2"> Value</e1>
<e2 att="test1" att2="test2"> Value 2 <inner class='i'>inner</inner></e2>
<e3 att="test1" att2="test2"> Value 3</e3>

</root>

在过滤后(删除了 e3 元素和 att2 属性):
<root>
<e1 att="test1" > Value</e1>
<e2 att="test1" > Value 2 <inner class='i'>inner</inner></e2>
</root>

注意:

  • 如果可能的话,我更喜欢使用for-each元素而不是apply-templates
  • 我在使用xsl:elementxsl:attribute时遇到了一些问题,因为我无法编写当前节点的名称

谢谢


1
你为什么更喜欢使用 for-each 而不是 apply-templates - Wayne
1
@lwburk - 我认为“我在xsl:element和xsl:attribute方面遇到了一些问题...”指向了一些更深层次的问题。 - Daniel Haley
1
不清楚您是在寻找通用解决方案(未知元素名称)还是特定解决方案(筛选器“e3”)。 我提供的答案将帮助您解决前一种情况,即使很容易适应特定情况。 - Emiliano Poggi
@lwburk:我认为for-each比apply-templates更接近编程语言的风格。 - Abdullah
2
一开始认为使用“for-each”是解决大多数问题的自然方式是很正常的,但我认为随着您对XSLT的熟悉程度增加,您会发现它完全是不必要的。 - Wayne
2个回答

9

我知道您更喜欢使用for-each,但为什么不使用身份转换,然后使用模板覆盖您不想保留的内容呢?

这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="e3|@att2"/>

</xsl:stylesheet>

生成:

<root>
   <e1 att="test1"> Value</e1>
   <e2 att="test1"> Value 2 <inner class="i">inner</inner>
   </e2>
</root>

@DevNull:如果最后一个节点不是e3,这个方法就行不通了。我对你的答案进行了泛化。 - Emiliano Poggi
1
@empo - 这是正确的,但我在原始帖子中没有看到任何关于无论名称如何都要删除最后一个节点的内容。原始帖子只指定了 e3att2 被移除。按照您的逻辑,我也可以假设 OP 试图删除所有名称以 "3" 结尾的元素。 - Daniel Haley
@DevNull:是的,除非直接感兴趣的人,否则谁知道呢 :D! - Emiliano Poggi
@DevNull,@empo:实际上我想要删除“e3”元素,就像@DevNull所说的那样,而不是最后一个元素。谢谢大家,你们的答案很完美。 - Abdullah
这是解决这个问题的正确方式,也是非常常见的模式。 - Wayne

1

正如@DevNull所示,使用身份转换更加简单和简洁。无论如何,这里是一个可能的解决方案,使用for-each而不使用apply-templates,就像您要求的那样:

<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="/root">
   <root>
    <xsl:for-each select="child::node()">
     <xsl:choose>
      <xsl:when test="position()=last()-1"/>
      <xsl:otherwise>
       <xsl:copy>
        <xsl:copy-of select="@att"/>
        <xsl:copy-of select="child::node()"/>
       </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
   </xsl:for-each>
  </root>
</xsl:template>


关于使用身份转换的说明

如果您的情况确实是看起来那样,也就是元素名称未知,@DevNull 将无法工作,您需要使用更通用的方法,例如:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
   <xsl:strip-space elements="*"/>

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

    <xsl:template match="root/child::node()[position()=last()]|@att2"/>

</xsl:stylesheet>

这个解决方案甚至可以处理最后一个元素e4e1000


非常感谢,实际上我想删除元素"e3"而不是最后一个元素,抱歉问题表述不清。你的解决方案在两种情况下都很完美...但我选择了@DevNull的解决方案,因为它在我的情况下运行良好。再次感谢你。 - Abdullah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接