按XML元素的属性排序

时间:2019-04-04 14:32:13

标签: xml sorting xslt xslt-3.0

我的问题与这些问题非常相似:

  1. List item
  2. List item
  3. List item
  4. List item
  5. List item

但是,他们没有为我的特殊情况提供答案。尽管最终我解决了问题,但是我觉得这种解决方案不是很好,如果有更好的方法可以解决,我将不胜感激。我第一次遇到排序问题,想更好地理解它。

从此(建模的)输入中:

<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 :(得分:0)

如果您只想对相邻的element元素进行排序,那么我认为使用xsl:for-each-group select="*" group-adjacent="boolean(self::element)处理元素可以识别它们,然后在for-each-group内部可以处理{{ 1}}元素是根据属性和其他元素进行排序的,而不进行排序:

element

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

如果您想对所有<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> 子元素进行排序并根据所有element子元素的原始位置交换它们,那么我认为以下内容将首先计算属性值的排序顺序,然后赋予每个要排序的元素原始输入顺序的位置,有助于:

element

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

对于支持高阶功能<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> 的XSLT 3处理器(例如Saxon PE或EE或Altova),我认为可以对原始排序顺序使用一系列元素或元素ID进行改进:

sort

这样,我认为即使存在具有相同排序键值(例如<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> 值)的各种元素,该方法也应该有效。