Сортировать XML-элементы по их атрибутам - PullRequest
0 голосов
/ 04 апреля 2019

Мой вопрос очень похож на те:

  1. Элемент списка
  2. Элемент списка
  3. Элемент списка
  4. Элемент списка
  5. Элемент списка

Однако они не дают ответа на мой конкретный случай. Хотя в конце концов я решил проблему, но я не чувствую, что это решение будет хорошим, и был бы признателен, если есть лучшие способы сделать это. Я впервые столкнулся с проблемой сортировки и хотел бы ее лучше понять.

С этого (смоделированного) входа:

<root>
    <measure attribute="attr">
        <other n="234">-</other>
        <other n="345">-</other>
        <element n="2"/>
        <element n="1"/>
        <element n="3"/>
        <other attr="abc">-</other>
    </measure>
    <measure>
        <other n="234">-</other>
        <other n="345"><node/></other>
        <element n="3"/>
        <element n="1"/>
        <element n="2"/>
        <other attr="abc">-</other>
    </measure>
</root>

Я хочу получить такой результат:

<root>
   <measure>
      <other n="234">-</other>
      <other n="345">-</other>
      <element n="1"/>
      <element n="2"/>
      <element n="3"/>
      <other attr="abc">-</other>
   </measure>
   <measure>
      <other n="234">-</other>
      <other n="345">
         <node/>
      </other>
      <node/>
      <element n="1"/>
      <element n="2"/>
      <element n="3"/>
      <other attr="abc">-</other>
   </measure>
</root>

Поэтому я хочу, чтобы отдельные элементы (<element/>) были отсортированы по отношению друг к другу, но другие элементы должны оставаться на своих местах.

Сначала я попробовал это: https://xsltfiddle.liberty -development.net / 6r5Gh3h / 3

<xsl:stylesheet  version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
    <xsl:strip-space elements="*"/>
    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="measure">  
        <xsl:copy>
            <xsl:apply-templates select="@*, node()[local-name()!='element']"/>
            <xsl:apply-templates  select="element">
                <xsl:sort order="ascending" select="@n"/>
            </xsl:apply-templates>
        </xsl:copy>     
    </xsl:template>
</xsl:stylesheet>

Но это изменило порядок элементов.

Это решение дает желаемый результат, но есть ли лучшие способы сделать это?

https://xsltfiddle.liberty -development.net / 94rmq6j

<xsl:stylesheet exclude-result-prefixes="xs math map array" version="3.0" xmlns:array="http://www.w3.org/2005/xpath-functions/array" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:strip-space elements="*"/>
<xsl:mode on-no-match="shallow-copy"/>

<xsl:template match="measure">
    <xsl:copy>
        <xsl:variable name="sortedEls">
            <xsl:perform-sort select="child::element">
                <xsl:sort data-type="number" order="ascending" select="@n"/>
            </xsl:perform-sort>
        </xsl:variable>

        <xsl:for-each select="descendant::*">
            <xsl:choose>
                <xsl:when test="local-name() = 'element' and not(following-sibling::element)">
                    <xsl:sequence select="$sortedEls"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:if test="local-name() != 'element'">
                        <xsl:apply-templates select="."/>
                    </xsl:if>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>

    </xsl:copy>
</xsl:template>

1 Ответ

0 голосов
/ 04 апреля 2019

Если вы хотите отсортировать только смежные element элементы, то, я думаю, обработка элементов с помощью xsl:for-each-group select="*" group-adjacent="boolean(self::element) позволяет вам идентифицировать их, а затем внутри for-each-group вы можете обрабатывать группы element элементов, отсортированных по атрибут и другие элементы без сортировки:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:array="http://www.w3.org/2005/xpath-functions/array"
    exclude-result-prefixes="xs math map array"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output indent="yes"/>

  <xsl:template match="measure">
      <xsl:copy>
          <xsl:for-each-group select="*" group-adjacent="boolean(self::element)">
              <xsl:choose>
                  <xsl:when test="current-grouping-key()">
                      <xsl:apply-templates select="current-group()">
                          <xsl:sort select="xs:decimal(@n)"/>
                      </xsl:apply-templates>
                  </xsl:when>
                  <xsl:otherwise>
                      <xsl:apply-templates select="current-group()"/>
                  </xsl:otherwise>                  
              </xsl:choose>
          </xsl:for-each-group>
       </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

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

Если вы хотите отсортировать все дочерние элементы element и поменять их местами на основе исходной позиции всех дочерних элементов element, я думаю следующее: сначала вычисляется отсортированная последовательность значений атрибутов, а затем каждый элемент получает сортируется позиция исходного порядка ввода, помогает:

<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 indent="yes"/>

  <xsl:template match="measure">
      <xsl:copy>
          <xsl:variable name="original-order" as="xs:string*" select="node()!generate-id()"/>
          <xsl:variable name="elems" as="element(element)*" select="element"/>
          <xsl:variable name="sort-order" as="xs:decimal*" select="sort(element/xs:decimal(@n))"/>
          <xsl:apply-templates>
              <xsl:sort
                select="if (. instance of element(element)) 
                        then 
                          let $sort-pos := index-of($sort-order, xs:decimal(@n)),
                              $orig-el := $elems[$sort-pos]
                          return
                              index-of($original-order, $orig-el!generate-id())
                        else position()"/>
          </xsl:apply-templates>
       </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty -development.net / bFN1y9m / 1

Для процессоров XSLT 3 с функцией высшего порядка sort поддержка (например, Saxon PE или EE или Altova) Я думаю, что это можно улучшить, если использовать последовательность элементов или идентификаторов элементов для исходного порядка сортировки:

<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:template match="measure">
      <xsl:copy>
          <xsl:variable name="original-order" as="xs:string*" select="node()!generate-id()"/>
          <xsl:variable name="elems" as="element(element)*" select="element"/>
          <xsl:variable name="sort-order" as="xs:decimal*" select="sort(element/xs:decimal(@n))"/>
          <xsl:apply-templates>
              <xsl:sort
                select="if (. instance of element(element)) 
                        then 
                          let $sort-pos := index-of($sort-order, xs:decimal(@n)),
                              $orig-el := $elems[$sort-pos]
                          return
                              index-of($original-order, $orig-el!generate-id())
                        else position()"/>
          </xsl:apply-templates>
       </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Таким образом, я думаю, что подход должен работать, даже если существуют различные элементы с одинаковым значением ключа сортировки (например, @n значение).

...