XSLT v1.0 - 如何对不均匀属性进行分组?

时间:2014-01-22 13:43:03

标签: xml xslt-1.0 grouping muenchian-grouping

我正在尝试使用xs元素上的xslt对不真正共享任何公共值的属性实现分组,我不确定我是否应该使用muenchian grouping,尽管我已经将我的元素分组了我的代码中的这个方法。我也在论坛中搜索但没有运气,因为大多数分组似乎发生在具有共同值的属性上。

更具体地说,我想要实现的是在pdf上打印,一行显示多个记录,这些记录具有属性为具有值“PC”属性Ty(Att ty =“PC”)的Att元素的特定ID。所有这些都应该与我现有的分组一起发生。

我的xml代码示例:

<Document>
    <RecordDetails>
        <Record>
            <contact id="0001" title="Mr" forename="John" surname="Smith" ST='M'/>
            <AggSet>                
                <Att Ty="Addr" Id="43 Menelaou Street" />
                <Att Ty="PC"   Id="15230" />
                <Att Ty="Num"  Id="2580052635" />             
            </AggSet>
            <Charge Amount="3.000" PT="P" />
        </Record>
        <Record>
            <contact id="0001" title="Mr" forename="John" surname="Smith" ST='M'/>
            <AggSet>                
                <Att Ty="Addr" Id="65 Dankan Street" />
                <Att Ty="PC"   Id="15236" />
                <Att Ty="Num"  Id="2580052635" />             
            </AggSet>
            <Charge Amount="10.000" PT="P" />
        </Record>
        <Record>
            <contact id="0002" title="Dr" forename= "Amy" surname="Jones" ST='Y'/>
            <AggSet>                
                <Att Ty="Addr" Id="28 Karman Street" />
                <Att Ty="PC"   Id="15237" />
                <Att Ty="Num"  Id="2584552635" />             
            </AggSet>
            <Charge Amount="-2.000" PT="P" />
        </Record>
        <Record>    
            ...
        </Record>
    </RecordDetails>
</Document>

因此,例如对于记录2,3我想打印只有1行,因为他们的邮政编码对我来说属于同一区域,因为Ty =“PC”表示发布,我正在尝试分组更大的区域基础。

我在Apache FOP上使用了跟随xsl:

<xsl:key name="ct" match="Record[Charge/@PT='P']" use="@ST"/>


<xsl:template match ="RecordDetails">
    <xsl:for-each select="Record[generate-id(.)=generate-id(key('ct',@ST)[1])]">
        <xsl:if test="@ST='M' and (./AggSet/Att[@Ty='PC']/@Id='15236' or ./AggSet/Att[@Ty='TZ']/@Id='15237' or ... )  ">
            <fo:table-row>                          
                    <xsl:apply-templates select="."/>                            
            </fo:table-row>
        </xsl:if> 
        <xsl:for-each select="key('ct',@ST)">                       
            <xsl:choose>                                    
                 <xsl:when test="@ST='M' and (./AggSet/Att[@Ty='PC']/@Id='15236' or ./AggSet/Att[@Ty='TZ']/@Id='15237' or ... )  "> 
                 </xsl:when>                     
                 <xsl:otherwise>
                  <fo:table-row>
                    <xsl:apply-templates select="."/>
                  </fo:table-row>
                 </xsl:otherwise>
            </xsl:choose>             
        </xsl:for-each>                             
    </xsl:for-each>
</xsl:template>

<xsl:template match="Record">
    <fo:table-cell>
        <xsl:choose>
            <xsl:when test="@ST='M' and (./AggSet/Att[@Ty='PC']/@Id='15236' or ./AggSet/Att[@Ty='TZ']/@Id='15237' or ... )">
                <fo:block text-align="center">
                    <xsl:text>Greater area</xsl:text>
                </fo:block>
            </xsl:when>
            <xsl:otherwise>
                <fo:block text-align="center">
                    <xsl:value-of select="./AggSet/Att[@Ty='PC']/@Id" />
                </fo:block>
            </xsl:otherwise>
        </xsl:choose>
    </fo:table-cell>
    <fo:table-cell>
        <xsl:choose>
            <xsl:when test="@ST='M' and (./AggSet/Att[@Ty='PC']/@Id='15236' or ./AggSet/Att[@Ty='TZ']/@Id='15237' or ... )">
                <fo:block text-align="center">
                    <xsl:value-of select="sum(//Record[@ST='M' and (./AggSet/Att[@Ty='PC']/@Id='15236' or ./AggSet/Att[@Ty='TZ']/@Id='15237' or ... )]/contact/Charge/@Amount)" />
                </fo:block>
            </xsl:when>
            <xsl:otherwise>
                <fo:block text-align="center">
                    <xsl:value-of select="./Charge/@Amount" />
                </fo:block>
            </xsl:otherwise>
        </xsl:choose>
    </fo:table-cell>
</xsl:template>

虽然我过去已经为实际在我现有分组中共享公共属性值的元素实现了这个逻辑,但上面的代码根本没有为我想要的聚合提供任何行,我想知道我的OR是否有问题条件因某种原因而变得虚假。

我错过了什么吗? 任何帮助将不胜感激,

由于

编辑: 正如Tomalak在我的案例中指出的那样,我要做的是实现手动组,这意味着代码中确实存在硬编码条件。现在没有通用的方法为我计算这些值。

1 个答案:

答案 0 :(得分:1)

这种方法怎么样:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:my="http://tempuri.org/config"
  exclude-result-prefixes="my"
>
  <my:config>
    <PC_group>
      <item>15236</item>
      <item>15237</item>
    </PC_group>
    <!-- more groups like this... -->
  </my:config>

  <!-- create a reference to our own config -->
  <xsl:variable name="config" select="document('')/*/my:config" />
  <xsl:variable name="PC_group" select="$config/PC_group" />

  <xsl:template match="RecordDetails">
    <grouped_RecordDetails>
      <xsl:apply-templates mode="group" select="Record[Charge/@PT='P']" />
    </grouped_RecordDetails>
  </xsl:template>

  <xsl:template match="Record" mode="group">
    <xsl:variable name="myPC" select="AggSet/Att[@Ty = 'PC']/@Id" />

    <!-- select all the PCs in this group -->
    <xsl:variable name="groupPCs" select="$PC_group[item = $myPC]/item" />

    <!-- identify all other members of this group -->
    <xsl:variable name="groupMembers" select=". | ../Record[
      Charge/@PT='P' and AggSet/Att[@Ty = 'PC']/@Id = $groupPCs
    ]" />

    <!-- do the actual grouping, just like the Muenchian method... -->
    <xsl:if test="generate-id() = generate-id($groupMembers[1])">

      <!--
        we are at the first Record in this group now
        all the other group members are at $groupMembers
        output whatever details you like here
      -->
      <xsl:copy-of select="." />

    </xsl:if>
  </xsl:template>

  <xsl:template match="text()[normalize-space() = '']" />

</xsl:stylesheet>

示例输出

<grouped_RecordDetails>
  <Record>
    <contact id="0001" title="Mr" forename="John" surname="Smith" ST="M" />
    <AggSet>
      <Att Ty="Addr" Id="43 Menelaou Street" />
      <Att Ty="PC" Id="15230" />
      <Att Ty="Num" Id="2580052635" />
    </AggSet>
    <Charge Amount="3.000" PT="P" />
  </Record>
  <Record>
    <contact id="0001" title="Mr" forename="John" surname="Smith" ST="M" />
    <AggSet>
      <Att Ty="Addr" Id="65 Dankan Street" />
      <Att Ty="PC" Id="15236" />
      <Att Ty="Num" Id="2580052635" />
    </AggSet>
    <Charge Amount="10.000" PT="P" />
  </Record>
</grouped_RecordDetails>

编辑:当然,如果您愿意,可以在一个大而凌乱的XPath表达式中完成分组:

<!-- identify all members of this group -->
<xsl:variable name="groupMembers" select="
  . | ../Record[
    Charge/@PT = 'P' 
    and AggSet/Att[@Ty = 'PC']/@Id = $PC_group[
          item = current()/AggSet/Att[@Ty = 'PC']/@Id
        ]/item
  ]
" />

将它拆分成几个变量使得更容易理解。