转换以删除重复和复制休息

时间:2012-06-12 16:50:20

标签: xml xslt xslt-1.0

我想根据属性'f'将输出xml分组为元素'c'。这是我的输入xml和xslt。我希望组只出现一次,其他节点应该按原样复制到输出。我试过的xslt,复制整个输入xml。因此,如果有两个或更多元素具有c元素和'f'的相同属性值,则希望该组的第一个出现在输出中。我想要的结果也被复制了。

输入xml

<M>
   <a>
      <b>
         <c f="123">
            <d>Al</d>
            <e NO="678">
               <f>Y</f>
               <g>
                  <h>FTO</h>
               </g>
            </e>
         </c>
      </b>
   </a>
  <a>
    <b>
      <c f="123">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <c f="567">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <somethingelse></somethingelse>
    </b>
  </a>
</M>

想要输出xml

<M>
  <a>
    <b>
      <c f="123">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <c f="567">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <somethingelse></somethingelse>
    </b>
  </a>
</M>

xslt我试过

<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="mykey" match="c"
   use="@f"/>

  <xsl:template match=
  "c[generate-id()
      =
       generate-id(key('mykey',@f)[1])
      ]
  ">



    <xsl:text/>
    <xsl:copy-of select="key('mykey',@f)[1]"/>
  </xsl:template>
  <xsl:template match="node()|@*">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

4 个答案:

答案 0 :(得分:2)

此转化

<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="kAByC-F" match="a" use="*/c/@f"/>

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

     <xsl:template match=
      "a[*/c
       and
         not(generate-id()
            =
             generate-id(key('kAByC-F', */c/@f)[1])
             )
        ]"/>
</xsl:stylesheet>

应用于提供的XML文档

<M>
   <a>
      <b>
         <c f="123">
            <d>Al</d>
            <e NO="678">
               <f>Y</f>
               <g>
                  <h>FTO</h>
               </g>
            </e>
         </c>
      </b>
   </a>
  <a>
    <b>
      <c f="123">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <c f="567">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <somethingelse></somethingelse>
    </b>
  </a>
</M>

生成想要的正确结果

<M>
   <a>
      <b>
         <c f="123">
            <d>Al</d>
            <e NO="678">
               <f>Y</f>
               <g>
                  <h>FTO</h>
               </g>
            </e>
         </c>
      </b>
   </a>
   <a>
      <b>
         <c f="567">
            <d>Al</d>
            <e NO="678">
               <f>Y</f>
               <g>
                  <h>FTO</h>
               </g>
            </e>
         </c>
      </b>
   </a>
   <a>
      <b>
         <somethingelse/>
      </b>
   </a>
</M>

<强>解释

正确使用 Muenchian grouping method

答案 1 :(得分:1)

一个简单的解决方案是为所有后续c节点添加一个空模板:

<xsl:template match="c[generate-id() = generate-id(key('mykey',@f)[position() &gt; 1])]" />

答案 2 :(得分:0)

一个想法可能是以一种允许您将它们彼此区分开来的格式保存变量中的c的所有值,然后每次遇到c时,检查该值是否包含在变量中。如果是,请跳到下一个节点。如果不是,则继续处理当前节点。

告诉我您是否需要更具体的信息

编辑:作为一种替代方案,可能是一种更简单的方法(我最近一直在使用NAnt,所以我可能会给你一个NAnt策略)是按照s值对所有节点进行排序。然后只需要一个变量来存储c的当前值并进行比较,直到你正在查看的值不等于存储的值。然后重新分配值并再次执行!

答案 3 :(得分:0)

可以匹配<a>的元素和检查是否有与他们的f子元素相同的<c>属性前述任一兄弟姐妹。如果有,您发现给定f值的重复(给定f值的出现不是该值的第一次出现)并且您可以将身份模板覆盖为跳过元素:

<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()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/M/a[b/c/@f = preceding-sibling::a/b/c/@f]"/>

</xsl:stylesheet>

此解决方案的一个优点是它不需要任何关于密钥或ID生成的知识;它只适用于基本的XPath轴功能。但是,当要比较的元素并非全部位于相同的嵌套深度/相同的相对元素层次结构中时,它可能会稍微复杂一些。

PS:我删除了<xsl:strip-space elements="*"/>元素,因为我无法测试它(我的Xml处理器声称我只能使用它,如果我传递一个可读的流而不是文件),但可以随意重新插入如果它适合你。