XSL根据Element值删除前一个兄弟

时间:2012-07-20 08:36:48

标签: xslt

您好,我需要帮助解析以下XML。

<xmeml>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>1234</Unit2>
    </Test>
    <Test>
        <Unit>bcd</Unit>
        <Unit2>2345</Unit2>
    </Test>
</Doc>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>3456</Unit2>
    </Test>
    <Test>
        <Unit>cde</Unit>
        <Unit2>3456</Unit2>
    </Test> 
</Doc>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>1234</Unit2>
    </Test>
    <Test>
        <Unit>def</Unit>
        <Unit2>4567</Unit2>
    </Test> 
</Doc>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>1234</Unit2>
    </Test>
    <Test>
        <Unit>efg</Unit>
        <Unit2>2345</Unit2>
    </Test> 
</Doc>
</xmeml>

以下面的

结束
<xmeml>
<Doc>
    <Test>
        <Unit>bcd</Unit>
        <Unit2>2345</Unit2>
    </Test>
</Doc>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>3456</Unit2>
    </Test>
    <Test>
        <Unit>cde</Unit>
        <Unit2>3456</Unit2>
    </Test> 
</Doc>
<Doc>
    <Test>
        <Unit>def</Unit>
        <Unit2>4567</Unit2>
    </Test> 
</Doc>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>1234</Unit2>
    </Test>
    <Test>
        <Unit>efg</Unit>
        <Unit2>2345</Unit2>
    </Test> 
</Doc>
</xmeml>

我正在尝试创建一个XSLT文档来执行此操作但尚未找到一个有效的文档。 我应该注意,其中所需的匹配参数(在本例中为“abc”)是变量,并且永远不会是静态可搜索实体。

所以在英语中我的XSL会是这样的: 对于包含匹配“单位”值的任何父级  删除除“最后一个”之外的“单位”内包含重复值的所有前面的父项“测试”。

所有帮助最受赞赏 感谢

2 个答案:

答案 0 :(得分:2)

您可以使用标识模板复制整个文档,并使用空模板覆盖该模板,以查找要删除的元素。要检查是否应删除<Test>元素,您可以将其<Unit>值与后续兄弟元素的值进行比较。

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

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

    <xsl:template match="/xmeml/Doc/Test[(Unit = following-sibling::Test/Unit) or (Unit = ../following-sibling::Doc/Test/Unit)][descendant::Unit2[starts-with(.,'1234')]]"/>

</xsl:stylesheet>

由于存在两种<Unit>值后续值的可能性,因此两种条件都是明确写入的,并在条件中与or运算符连接。

两种可能性及其各自的XPath条件是:

  • 在与当前元素相同的父元素中的后续<Test>元素中:
    Unit = following-sibling::Test/Unit
  • 位于当前<Test>元素的后续<Doc>兄弟中的<Doc>元素中:
    Unit = ../following-sibling::Doc/Test/Unit

假设这个输入:

<xmeml>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>1234</Unit2>
    </Test>
    <Test>
        <Unit>bcd</Unit>
        <Unit2>2345</Unit2>
    </Test>
</Doc>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>3456</Unit2>
    </Test>
    <Test>
        <Unit>cde</Unit>
        <Unit2>3456</Unit2>
    </Test> 
</Doc>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>1234</Unit2>
    </Test>
    <Test>
        <Unit>def</Unit>
        <Unit2>4567</Unit2>
    </Test> 
</Doc>
<Doc>
    <Test>
        <Unit>abc</Unit>
        <Unit2>1234</Unit2>
    </Test>
    <Test>
        <Unit>efg</Unit>
        <Unit2>2345</Unit2>
    </Test> 
</Doc>
</xmeml>

XSLT创建此输出:

<xmeml>
  <Doc>
    <Test>
      <Unit>bcd</Unit>
      <Unit2>2345</Unit2>
    </Test>
  </Doc>
  <Doc>
    <Test>
      <Unit>abc</Unit>
      <Unit2>3456</Unit2>
    </Test>
    <Test>
      <Unit>cde</Unit>
      <Unit2>3456</Unit2>
    </Test>
  </Doc>
  <Doc>
    <Test>
      <Unit>def</Unit>
      <Unit2>4567</Unit2>
    </Test>
  </Doc>
  <Doc>
    <Test>
      <Unit>abc</Unit>
      <Unit2>1234</Unit2>
    </Test>
    <Test>
      <Unit>efg</Unit>
      <Unit2>2345</Unit2>
    </Test>
  </Doc>
</xmeml>

答案 1 :(得分:1)

在XSLT 1.0中有两种不同的分组方式 - 简单,使用preceding::following::轴,以及更有效的Muenchian分组方法 - 使用密钥。

以下是效率更高的Muenchian方法:

<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:key name="kParentByUnit" match="Test[Unit]" use="Unit"/>

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

     <xsl:template match="Test[Unit]">
       <xsl:if test=
       "generate-id()
       =
        generate-id(key('kParentByUnit', Unit)[position()=last()])">

        <xsl:call-template name="identity"/>
       </xsl:if>
     </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<xmeml>
    <Doc>
        <Test>
            <Unit>abc</Unit>
            <Unit2>1234</Unit2>
        </Test>
        <Test>
            <Unit>bcd</Unit>
            <Unit2>2345</Unit2>
        </Test>
    </Doc>
    <Doc>
        <Test>
            <Unit>abc</Unit>
            <Unit2>3456</Unit2>
        </Test>
        <Test>
            <Unit>cde</Unit>
            <Unit2>3456</Unit2>
        </Test>
    </Doc>
    <Doc>
        <Test>
            <Unit>abc</Unit>
            <Unit2>1234</Unit2>
        </Test>
        <Test>
            <Unit>def</Unit>
            <Unit2>4567</Unit2>
        </Test>
    </Doc>
    <Doc>
        <Test>
            <Unit>abc</Unit>
            <Unit2>1234</Unit2>
        </Test>
        <Test>
            <Unit>efg</Unit>
            <Unit2>2345</Unit2>
        </Test>
    </Doc>
</xmeml>

产生了想要的正确结果

<xmeml>
   <Doc>
      <Test>
         <Unit>bcd</Unit>
         <Unit2>2345</Unit2>
      </Test>
   </Doc>
   <Doc>
      <Test>
         <Unit>cde</Unit>
         <Unit2>3456</Unit2>
      </Test>
   </Doc>
   <Doc>
      <Test>
         <Unit>def</Unit>
         <Unit2>4567</Unit2>
      </Test>
   </Doc>
   <Doc>
      <Test>
         <Unit>abc</Unit>
         <Unit2>1234</Unit2>
      </Test>
      <Test>
         <Unit>efg</Unit>
         <Unit2>2345</Unit2>
      </Test>
   </Doc>
</xmeml>