XSL:根据属性计数和匹配来创建组

时间:2019-04-10 01:38:01

标签: xml xslt count grouping xslt-1.0

我正在尝试在属性的计数= X时插入一个容器,并基于属性的值插入第二组。这两个属性不相关。

使用XSLT- V1

我想首先基于属性的值进行分组。就是任何时候ID = 01都会创建一个组。然后,我想在count = X时插入一个新的属性/容器。

我能够基于属性值进行分组,但是不确定如何确定计数并添加新容器。

我有如下所示的XML:

<Items>
  <Details>
    <ID>01</ID>
    <Name>Name for 01</Name>
    <Owner>User1</Owner>
    <Rev>01-A</Rev>
    <Rev_Owner>User2</Rev_Owner>
    <Rev_Code>US</Rev_Code>
  </Details>
  <Details>
    <ID>01</ID>
    <Name>Name for 01</Name>
    <Owner>User1</Owner>
    <Rev>01-B</Rev>
    <Rev_Owner>User3</Rev_Owner>
    <Rev_Code>CN</Rev_Code>
  </Details>
  <Details>
    <ID>02</ID>
    <Name>Name for 02</Name>
    <Owner>User1</Owner>
    <Rev>02-A</Rev>
    <Rev_Owner>User4</Rev_Owner>
    <Rev_Code>MX</Rev_Code>
  </Details>
  <Details>
    <ID>03</ID>
    <Name>Name for 03</Name>
    <Owner>User1</Owner>
    <Rev>03-A</Rev>
    <Rev_Owner>User5</Rev_Owner>
    <Rev_Code>CA</Rev_Code>
  </Details>
  <Details>
    <ID>02</ID>
    <Name>Name for 02</Name>
    <Owner>User1</Owner>
    <Rev>02-B</Rev>
    <Rev_Owner>User5</Rev_Owner>
    <Rev_Code>AU</Rev_Code>
  </Details>
  <Details>
    <ID>01</ID>
    <Name>Name for 01</Name>
    <Owner>User1</Owner>
    <Rev>02-C</Rev>
    <Rev_Owner>User5</Rev_Owner>
    <Rev_Code>JP</Rev_Code>
  </Details>
</Items>

我在XSL下方创建了用于商品ID的预期组


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

  <xsl:key name="ItemGroup" match="Details" use="ID"/>

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

  <xsl:template match="Details[generate-id()=generate-id(key('ItemGroup',ID)[1])]">
    <ItemID name="{ID}">
      <xsl:copy-of select="key('ItemGroup',ID)"/>
    </ItemID>
  </xsl:template>
  <xsl:template match="Details[not(generate-id()=generate-id(key('ItemGroup',ID)[1]))]"/>
</xsl:stylesheet>

Aboe XSL的输出:

<Items>
  <ItemID name="01">
      <Details>
         <ID>01</ID>
         <Name>Name for 01</Name>
         <Owner>User1</Owner>
         <Rev>01-A</Rev>
         <Rev_Owner>User2</Rev_Owner>
         <Rev_Code>US</Rev_Code>
      </Details>
      <Details>
         <ID>01</ID>
         <Name>Name for 01</Name>
         <Owner>User1</Owner>
         <Rev>01-B</Rev>
         <Rev_Owner>User3</Rev_Owner>
         <Rev_Code>CN</Rev_Code>
      </Details>
      <Details>
         <ID>01</ID>
         <Name>Name for 01</Name>
         <Owner>User1</Owner>
         <Rev>02-C</Rev>
         <Rev_Owner>User5</Rev_Owner>
         <Rev_Code>JP</Rev_Code>
      </Details>
   </ItemID>

  <ItemID name="02">
      <Details>
         <ID>02</ID>
         <Name>Name for 02</Name>
         <Owner>User1</Owner>
         <Rev>02-A</Rev>
         <Rev_Owner>User4</Rev_Owner>
         <Rev_Code>MX</Rev_Code>
      </Details>
      <Details>
         <ID>02</ID>
         <Name>Name for 02</Name>
         <Owner>User1</Owner>
         <Rev>02-B</Rev>
         <Rev_Owner>User5</Rev_Owner>
         <Rev_Code>AU</Rev_Code>
      </Details>
   </ItemID>
  <ItemID name="03">
      <Details>
         <ID>03</ID>
         <Name>Name for 03</Name>
         <Owner>User1</Owner>
         <Rev>03-A</Rev>
         <Rev_Owner>User5</Rev_Owner>
         <Rev_Code>CA</Rev_Code>
      </Details>
   </ItemID>


</Items>

例如,我现在想添加一个用于“详细信息” = 3的变量(它的确在1,000-5,000之间),然后期望低于输出

<Items>
  <Split>
      <ItemID name="01">
      <Details>
        <ID>01</ID>
        <Name>Name for 01</Name>
        <Owner>User1</Owner>
        <Rev>01-A</Rev>
        <Rev_Owner>User2</Rev_Owner>
        <Rev_Code>US</Rev_Code>
      </Details>
      <Details>
        <ID>01</ID>
        <Name>Name for 01</Name>
        <Owner>User1</Owner>
        <Rev>01-B</Rev>
        <Rev_Owner>User3</Rev_Owner>
        <Rev_Code>CN</Rev_Code>
      </Details>
      <Details>
        <ID>01</ID>
        <Name>Name for 01</Name>
        <Owner>User1</Owner>
        <Rev>02-C</Rev>
        <Rev_Owner>User5</Rev_Owner>
        <Rev_Code>JP</Rev_Code>
      </Details>
      </ItemID>
  </Split>
  <Split>
  <ItemID name="02">
    <Details>
      <ID>02</ID>
      <Name>Name for 02</Name>
      <Owner>User1</Owner>
      <Rev>02-A</Rev>
      <Rev_Owner>User4</Rev_Owner>
      <Rev_Code>MX</Rev_Code>
    </Details>
    <Details>
      <ID>02</ID>
      <Name>Name for 02</Name>
      <Owner>User1</Owner>
      <Rev>02-B</Rev>
      <Rev_Owner>User5</Rev_Owner>
      <Rev_Code>AU</Rev_Code>
    </Details>
  </ItemID>
  <ItemID name="03">
    <Details>
      <ID>03</ID>
      <Name>Name for 03</Name>
      <Owner>User1</Owner>
      <Rev>03-A</Rev>
      <Rev_Owner>User5</Rev_Owner>
      <Rev_Code>CA</Rev_Code>
    </Details>
  </ItemID>
  </Split>
  <Split>
     continued....

</Items>

非常感谢!

2 个答案:

答案 0 :(得分:0)

假设至少可以使用XSLT 2,您可以使用两个分组步骤,第一步是对group-by子元素进行简单的ID分组,第二步然后对第一步的结果进行位置分组:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

   <xsl:param name="split-size" as="xs:integer" select="3"/>

   <xsl:output indent="yes"/>

   <xsl:template match="Items">
       <xsl:copy>
           <xsl:variable name="groups">
               <xsl:for-each-group select="Details" group-by="ID">
                   <ItemID name="{current-grouping-key()}">
                       <xsl:copy-of select="current-group()"/>
                   </ItemID>
               </xsl:for-each-group>
           </xsl:variable>
           <xsl:for-each-group select="$groups/ItemID/Details" group-adjacent="(position() - 1) idiv $split-size">
               <split>
                   <xsl:copy-of select="current-group()/.."/>
               </split>
           </xsl:for-each-group>
       </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bFN1y9n

对于XSLT 1,要执行两步转换,您需要使用exsl:node-set或类似方法(取决于所使用的特定XSLT处理器)将结果树片段从第一分组步骤转换回一个节点集这样您就可以选择和浏览它;此外,位置“分组”或拆分需要沿同级轴进行一些选择:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    exclude-result-prefixes="exsl"
    version="1.0">

   <xsl:param name="split-size" select="3"/>

   <xsl:key name="group" match="Details" use="ID"/>

   <xsl:output indent="yes"/>
   <xsl:strip-space elements="*"/>

   <xsl:template match="Items">
       <xsl:copy>
           <xsl:variable name="groups">
               <xsl:for-each select="Details[generate-id() = generate-id(key('group', ID)[1])]">
                   <ItemID name="{ID}">
                       <xsl:copy-of select="key('group', ID)"/>
                   </ItemID>
               </xsl:for-each>
           </xsl:variable>
           <xsl:variable name="Details" select="exsl:node-set($groups)/ItemID/Details"/>
           <xsl:for-each select="$Details[position() mod $split-size = 1]">
               <split>
                   <xsl:copy-of select="(. | (following-sibling::Details | ../following-sibling::ItemID/Details)[position() &lt; $split-size])/.."/>
               </split>
           </xsl:for-each>
       </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bFN1y9n/2

答案 1 :(得分:0)

除了您现有的密钥外,我认为您还需要另一个密钥(首先使用该密钥),以根据详细信息是否具有ID = 01来对明细进行分组

<xsl:key name="ItemGroupOne" match="Details" use="ID = '01'"/>

尝试使用此XSLT

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

  <xsl:key name="ItemGroupOne" match="Details" use="ID = '01'"/>
  <xsl:key name="ItemGroup" match="Details" use="ID"/>

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

  <xsl:template match="Details[generate-id()=generate-id(key('ItemGroupOne',ID = '01')[1])]">
    <Split>
      <xsl:apply-templates select="key('ItemGroupOne',ID = '01')" mode="items" />
    </Split>
  </xsl:template>

  <xsl:template match="Details[generate-id()=generate-id(key('ItemGroup',ID)[1])]" mode="items">
    <ItemID name="{ID}">
      <xsl:copy-of select="key('ItemGroup',ID)"/>
    </ItemID>    
  </xsl:template>

  <xsl:template match="Details"/>

  <xsl:template match="Details" mode="items"/>
</xsl:stylesheet>

此处使用mode是为了避免模板冲突。

还请注意,对于忽略Details的最终模板,此处的条件中不需要not逻辑,因为与元素匹配条件的模板的优先级高于不仅仅是匹配无条件的元素。

在这里尝试:http://xsltfiddle.liberty-development.net/gWvjQfr

或者,如果您想删除对“模式”和忽略元素的模板的使用,也许可以这样写:

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

  <xsl:key name="ItemGroupOne" match="Details" use="ID = '01'"/>
  <xsl:key name="ItemGroup" match="Details" use="ID"/>

  <xsl:template match="/*">
    <Items>
      <xsl:apply-templates select="Details[generate-id()=generate-id(key('ItemGroupOne',ID = '01')[1])]" />
    </Items>
  </xsl:template>

  <xsl:template match="Details">
    <Split>
      <xsl:for-each select="key('ItemGroupOne',ID = '01')[generate-id()=generate-id(key('ItemGroup',ID)[1])]">
        <ItemID name="{ID}">
          <xsl:copy-of select="key('ItemGroup',ID)"/>
        </ItemID>    
      </xsl:for-each>
    </Split>
  </xsl:template>
</xsl:stylesheet>