XSL: создание группы на основе количества атрибутов и соответствия - PullRequest
0 голосов
/ 10 апреля 2019

Я пытаюсь вставить контейнер, когда количество атрибутов = X и 2-я группа основана на значении атрибута.Два атрибута не связаны.

Использование XSLT-V1

Я хотел бы сначала сгруппировать на основе значения атрибута.То есть.ID в любое время = 01 создаст группу.Затем я хотел бы вставить новый атрибут / контейнер, когда число = 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, который создает ожидаемую группу для идентификатора элемента


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 голосов
/ 10 апреля 2019

В дополнение к существующему ключу, я думаю, вам нужен еще один ключ (который будет использоваться первым), чтобы сгруппировать детали по тому, имеют ли они 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>
0 голосов
/ 10 апреля 2019

Предполагая, что по крайней мере в 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

...