我的问题与这些问题非常相似:
然而,他们并没有给出我的具体情况的答案。虽然最终我解决了这个问题,但我觉得这个解决方案不是很好,如果有更好的方法来解决这个问题,我会很感激。我第一次面对这类问题,我想更好地理解它。
来自此(建模)输入的:
<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>
发布于 2019-04-04 16:38:17
如果您只想对相邻的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
对于具有高阶函数sort支持的XSLT 3处理器(例如Saxon、EE或Altova),我认为这可以改进为使用元素序列或元素ids来处理原始排序顺序:
<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值)的各种元素,这种方法也应该有效。
https://stackoverflow.com/questions/55518584
复制相似问题