4

嗨,我是 Xslt/Xml 的新手。

我有这样的 XML:

<entry>
 <attribute1>A</attribute1>
 <attribute2>B</attribute2>
</entry>
<entry>
 <attribute1>A</attribute1>
 <attribute2>B</attribute2>
</entry>
<entry>
 <attribute1>C</attribute1>
 <attribute2>D</attribute2>
</entry>
<entry>
 <attribute1>E</attribute1>
 <attribute2>F</attribute2>
</entry>
...

我需要表格输出:

一种

Attribute1 Attribute2 Qty
   A           B       2
   C           D       1
   E           F       1

我需要你的帮助,我不知道如何计算唯一条目并将其显示为表格中的一个。

我正在使用 XSLT 1.0 版

4

2 回答 2

4

把这个简短,作为起点:

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

    <xsl:key name="keys" match="entry" use="concat(attribute1,'|',attribute2)"/>

    <xsl:template match="/test">
        <xsl:apply-templates select="entry[
          generate-id() 
          = generate-id(key('keys',concat(attribute1,'|',attribute2))[1])]"/>
    </xsl:template>

    <xsl:template match="entry">
        <xsl:value-of select="concat(
            attribute1,' ', 
            attribute2,' ',
            count(key('keys',concat(attribute1,'|',attribute2))),'&#10;')"/>
    </xsl:template> 

</xsl:stylesheet>

如果您是 XSLT 的新手,要了解这种转换,您需要阅读以下内容:

  • xslt 输出方法
  • xsl:关键元素
  • Meunchian 分组
  • xpath 连接和计数功能
  • 路径 | 联盟
于 2012-06-15T15:31:41.010 回答
3

I. 简单的 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:key name="kEntryByChildren" match="entry" use="."/>

 <xsl:template match=
 "entry[not(generate-id() = generate-id(key('kEntryByChildren', .)[1]))]"/>

 <xsl:template match="entry">
  <tr>
   <xsl:apply-templates/>
   <td><xsl:value-of select="count(key('kEntryByChildren', .))"/></td>
  </tr>
 </xsl:template>

 <xsl:template match="entry/*">
   <td><xsl:value-of select="."/></td>
 </xsl:template>

 <xsl:template match="/*">
   <table>
     <xsl:apply-templates/>
   </table>
 </xsl:template>
</xsl:stylesheet>

当应用于提供的 XML 时(片段被包装到单个顶部元素以获得格式良好的 XML 文档):

<t>
    <entry>
        <attribute1>A</attribute1>
        <attribute2>B</attribute2>
    </entry>
    <entry>
        <attribute1>A</attribute1>
        <attribute2>B</attribute2>
    </entry>
    <entry>
        <attribute1>C</attribute1>
        <attribute2>D</attribute2>
    </entry>
    <entry>
        <attribute1>E</attribute1>
        <attribute2>F</attribute2>
    </entry>
</t>

产生想要的正确结果

<table>
   <tr>
      <td>A</td>
      <td>B</td>
      <td>2</td>
   </tr>
   <tr>
      <td>C</td>
      <td>D</td>
      <td>1</td>
   </tr>
   <tr>
      <td>E</td>
      <td>F</td>
      <td>1</td>
   </tr>
</table>

当应用于这个棘手的 XML 文档时(如果我们使用子值的简单串联,我们会错误地得出前三个entry元素“相同”的结论):

<t>
    <entry>
        <attribute1>AB</attribute1>
        <attribute2>C</attribute2>
    </entry>
    <entry>
        <attribute1>A</attribute1>
        <attribute2>BC</attribute2>
    </entry>
    <entry>
        <attribute1>A</attribute1>
        <attribute2>BC</attribute2>
    </entry>
    <entry>
        <attribute1>C</attribute1>
        <attribute2>D</attribute2>
    </entry>
    <entry>
        <attribute1>E</attribute1>
        <attribute2>F</attribute2>
    </entry>
</t>

产生正确的结果:

<table>
   <tr>
      <td>AB</td>
      <td>C</td>
      <td>1</td>
   </tr>
   <tr>
      <td>A</td>
      <td>BC</td>
      <td>2</td>
   </tr>
   <tr>
      <td>C</td>
      <td>D</td>
      <td>1</td>
   </tr>
   <tr>
      <td>E</td>
      <td>F</td>
      <td>1</td>
   </tr>
</table>

说明

正确使用孟池分组法

请注意

  1. 此解决方案不依赖于元素的名称和子元素的数量entry,因此如果有两个以上的子元素,或者预先命名未知的不同数量的子元素,则可以应用此解决方案。

  2. 这里我们假设只有当相同的孩子具有相同的值时,所有孩子的字符串值的串联是相同的。


二、完整的 XSLT 1.0 解决方案

如果无法保证上述假设 2.,这是一种可能的 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:template match="entry">
  <xsl:variable name="vChildrenFp">
    <xsl:for-each select="*">
     <xsl:value-of select="concat(., '+')"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="vPrecedingSame">
    <xsl:for-each select="preceding-sibling::entry">
     <xsl:variable name="vthisFP">
       <xsl:for-each select="*">
         <xsl:value-of select="concat(., '+')"/>
       </xsl:for-each>
     </xsl:variable>

     <xsl:if test="$vthisFP = $vChildrenFp">1</xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:if test="not(string($vPrecedingSame))">
      <xsl:variable name="vFollowingSame">
        <xsl:for-each select="following-sibling::entry">
         <xsl:variable name="vthisFP">
           <xsl:for-each select="*">
             <xsl:value-of select="concat(., '+')"/>
           </xsl:for-each>
         </xsl:variable>

         <xsl:if test="$vthisFP = $vChildrenFp">1</xsl:if>
        </xsl:for-each>
      </xsl:variable>

      <tr>
       <xsl:apply-templates/>
       <td><xsl:value-of select="string-length($vFollowingSame)+1"/></td>
      </tr>
  </xsl:if>
 </xsl:template>

 <xsl:template match="entry/*">
   <td><xsl:value-of select="."/></td>
 </xsl:template>

 <xsl:template match="/*">
   <table>
     <xsl:apply-templates/>
   </table>
 </xsl:template>
</xsl:stylesheet>

当应用于同一个 XML 文档(上图)时,会产生相同的正确结果:

<table>
   <tr>
      <td>A</td>
      <td>B</td>
      <td>2</td>
   </tr>
   <tr>
      <td>C</td>
      <td>D</td>
      <td>1</td>
   </tr>
   <tr>
      <td>E</td>
      <td>F</td>
      <td>1</td>
   </tr>
</table>

说明

  1. 对于每个entry元素,我们为其子元素生成一个“指纹”(FP),entry如果其前面的兄弟entry元素都没有相同的子元素指纹,则处理该元素。

  2. “相同”entry元素的计数以类似的方式完成——对于entry具有相同子 FP 值的任何后续兄弟元素,我们输出单个字符('1')。总计数是这样生成的字符串(“1”)的字符串长度加 1。


三、XSLT 2.0 解决方案

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:my="my:my" exclude-result-prefixes="my xs">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:param name="pExoticString" select="'+'"/>

 <xsl:template match="/*">
   <table>
     <xsl:for-each-group select="entry" group-by="my:fingerprint(.)">
       <tr>
           <xsl:apply-templates/>
           <td><xsl:value-of select="count(current-group())"/></td>
       </tr>
     </xsl:for-each-group>
   </table>
 </xsl:template>

 <xsl:template match="entry/*">
   <td><xsl:value-of select="."/></td>
 </xsl:template>

 <xsl:function name="my:fingerprint" as="xs:string">
  <xsl:param name="pParent" as="element()"/>

  <xsl:sequence select="string-join($pParent/*, $pExoticString)"/>
 </xsl:function>
</xsl:stylesheet>

这个简单的解决方案可以轻松处理复杂的情况。当应用于最后一个 XML 文档时,会产生所需的正确结果

<table>
   <tr>
            <td>AB</td>
            <td>C</td>
         <td>1</td>
   </tr>
   <tr>
            <td>A</td>
            <td>BC</td>
         <td>2</td>
   </tr>
   <tr>
            <td>C</td>
            <td>D</td>
         <td>1</td>
   </tr>
   <tr>
            <td>E</td>
            <td>F</td>
         <td>1</td>
   </tr>
</table>

说明

正确使用xsl:for-each-group,xsl:function和.current-group()string-join()

于 2012-06-16T01:36:47.963 回答