Применение второй группировки для всех отдельных emenets в группе - PullRequest
0 голосов
/ 30 августа 2018

Я использовал for-each-group, если хотите сгруппировать элементы по ключу, а затем применить вторую группировку, если они не являются частью какой-либо группы.

Мой образец xml

<items>
    <item id="123" name="Java">
        <price></price>
        <description></description> 
    </item>

    <item id="123" name="Java and XML">
        <price></price>
        <description></description> 
    </item>

    <item id="234" name="python">
        <price></price>
        <description></description> 
    </item>

    <item id="456" name="scala">
        <price></price>
        <description></description> 
    </item>

    <item id="" name="python">
        <price></price>
        <description></description> 
    </item>


    <item id="768" name="scala">
        <price></price>
        <description></description> 
    </item>

    <item id="891" name="angular">
        <price></price>
        <description></description> 
    </item>
  </items>

Сначала я хочу сгруппировать по id, и если в группе несколько элементов, тогда я сформирую группу, в противном случае я применю другую группировку с name, затем сформирую группу, и, наконец, если это не какая-либо группировка, тогда создать свою группу.

Вывод должен выглядеть примерно так

<items>
    <group>
        <item id="123" name="Java">
            <price></price>
            <description></description> 
        </item>

        <item id="123" name="Java and XML">
            <price></price>
            <description></description> 
        </item>
    </group>
    <group>
        <item id="234" name="python">
            <price></price>
            <description></description> 
        </item>
        <item id="" name="python">
            <price></price>
            <description></description> 
        </item>
    </group>
    <group>
        <item id="456" name="scala">
            <price></price>
            <description></description> 
        </item>
        <item id="768" name="scala">
            <price></price>
            <description></description> 
        </item>
    </group>
    <group>
        <item id="891" name="angular">
            <price></price>
            <description></description> 
        </item>
    </group>
</items>

Как применить группировку для тех, которых нет ни в одной группе? for-each-group подходит для этого.

Обновление

Я попробовал этот подход

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

    <xsl:variable name="nonMatched" as="element()*">
        <xsl:for-each-group select="./items/item" group-by="@id">           
            <xsl:if test="count(current-group())  lt 2">
                <xsl:sequence select="current-group()"/>
            </xsl:if>
        </xsl:for-each-group>
    </xsl:variable>

    <xsl:template match="/">
    <items>
       <xsl:for-each-group select="/items/item" group-by="@id">
            <xsl:if test="count(current-group()) gt 1">
                <group>
                <xsl:copy-of select="current-group()"/>
                </group>
            </xsl:if> 
        </xsl:for-each-group>

    <xsl:for-each-group select="$nonMatched" group-by="@name">
        <group>
        <xsl:copy-of select="current-group()"/>
        </group>
    </xsl:for-each-group>
    </items>
    </xsl:template>

1 Ответ

0 голосов
/ 30 августа 2018

Одним из способов было бы протолкнуть элементы через разные шаблоны, где вы используете ключи для определения групп и условий, которые у вас есть для групп, а затем сформировать группу для первого элемента в каждой группе и использовать пустой шаблон для другой предметы в каждой группе; обратите внимание, что следующее использует приоритет, наложенный порядком https://www.w3.org/TR/xslt-30/#conflict, поэтому порядок, используемый ниже для шаблонов, важен, хотя вы также можете использовать атрибуты priority, чтобы навязывать свои правила в пользу группировки на основе @id, тогда Группировка на основе @name, затем группируйте любой элемент (например, match="item"), на который не распространяются другие условия группировки:

<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:mode on-no-match="shallow-copy"/>

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

  <xsl:key name="id" match="item[normalize-space(@id)]" use="@id"/>
  <xsl:key name="name" match="item" use="@name"/>

  <xsl:template match="item">
      <group>
          <xsl:copy-of select="."/>
      </group>
  </xsl:template>

  <xsl:template match="item[key('name', @name)[2] and . is key('name', @name)[1]]">
      <group>
          <xsl:copy-of select="key('name', @name)"/>
      </group>
  </xsl:template>

  <xsl:template match="item[key('name', @name)[2] and not(. is key('name', @name)[1])]"/>

  <xsl:template match="item[key('id', @id)[2] and . is key('id', @id)[1]]">
      <group>
          <xsl:copy-of select="key('id', @id)"/>
      </group>
  </xsl:template>

  <xsl:template match="item[key('id', @id)[2] and not(. is key('id', @id)[1])]"/> 

</xsl:stylesheet>

Онлайн на https://xsltfiddle.liberty -development.net / bdxtqt , использует XSLT 3, но вы можете заменить объявление xsl:mode, использовавшееся в начале, шаблоном идентификации

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

чтобы он работал с процессором XSLT 2. Только предостережение о том, что процессор XSLT 2 может прибегнуть к сообщению об ошибке, если существует несколько совпадений вместо того, чтобы использовать последний соответствующий шаблон, я не помню, какой процессор XSLT 2 это делает, но, как было сказано, использование приоритетов решило бы это.

Ваш опубликованный подход также должен работать:

  <xsl:template match="items">
    <xsl:copy>
        <xsl:variable name="nonMatched" as="element()*">
            <xsl:for-each-group select="item" group-by="@id">           
                <xsl:sequence
                  select="if (not(current-group()[2]))
                          then .
                          else ()"/>
            </xsl:for-each-group>
        </xsl:variable>
        <xsl:for-each-group select="item except $nonMatched" group-by="@id">
            <group>
                <xsl:apply-templates select="current-group()"/>
            </group>
        </xsl:for-each-group>
        <xsl:for-each-group select="$nonMatched" group-by="@name">
            <group>
                <xsl:apply-templates select="current-group()"/>
            </group>
        </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

https://xsltfiddle.liberty -development.net / 3NzcBtv

...