XSLT排序需求:指定元素按序置顶,未定义元素后置
问题:XSLT指定元素排序,未匹配元素后置需求
不常使用XSLT,已实现子元素按指定顺序排序,但存在问题:当元素值不在排序列表中时,排序规则失效。期望实现5个指定子元素始终按特定顺序置于顶部,其余任意元素均排在后面(其余元素排序无要求)。
XML示例(所有元素值均在列表中时)
<Root> <Parent> <Child> <FieldValue>SAMPLE1</FieldValue> </Child> </Parent> <Parent> <Child> <FieldValue>SAMPLE2</FieldValue> </Child> </Parent> <Parent> <Child> <FieldValue>SAMPLE3</FieldValue> </Child> </Parent> <Parent> <Child> <FieldValue>SAMPLE4</FieldValue> </Child> </Parent> </Root>
当前XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:param name="pSortingValues" select="'SAMPLE2,SAMPLE1,SAMPLE4,SAMPLE3'"/> <xsl:variable name="vSortingValues" select= "concat(',', $pSortingValues, ',')"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="/*"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:apply-templates select="*"> <xsl:sort data-type="number" select= "string-length(substring-before($vSortingValues,concat(',',Child/FieldValue,',')))"/> </xsl:apply-templates> </xsl:copy> </xsl:template> </xsl:stylesheet>
执行结果(所有元素匹配时)
<Root> <Parent> <Child> <FieldValue>SAMPLE2</FieldValue> </Child> </Parent> <Parent> <Child> <FieldValue>SAMPLE1</FieldValue> </Child> </Parent> <Parent> <Child> <FieldValue>SAMPLE4</FieldValue> </Child> </Parent> <Parent> <Child> <FieldValue>SAMPLE3</FieldValue> </Child> </Parent> </Root>
解决方案
核心思路分两步:
- 第一层排序:标记元素是否在指定排序列表中,在列表的元素权重设为0,不在的设为1,强制指定元素排在顶部
- 第二层排序:对在列表中的元素,沿用原有的位置计算规则保持指定顺序;不在列表的元素无需额外排序(或按需自定义)
修改后的XSLT代码如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:param name="pSortingValues" select="'SAMPLE2,SAMPLE1,SAMPLE4,SAMPLE3'"/> <xsl:variable name="vSortingValues" select="concat(',', $pSortingValues, ',')"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="/*"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:apply-templates select="*"> <!-- 第一层排序:区分是否在指定列表,在则排前 --> <xsl:sort data-type="number" select=" contains($vSortingValues, concat(',', Child/FieldValue, ',')) 0 1 "/> <!-- 第二层排序:指定列表内的元素按原规则排序 --> <xsl:sort data-type="number" select=" string-length(substring-before($vSortingValues, concat(',', Child/FieldValue, ','))) "/> </xsl:apply-templates> </xsl:copy> </xsl:template> </xsl:stylesheet>
说明
- 第一层排序通过
contains判断元素值是否在带前后逗号的排序字符串中,返回0(在列表)或1(不在列表),确保指定元素全部排在前面 - 第二层排序沿用你原有的
substring-before长度计算逻辑,保证指定元素内部按预设顺序排列 - 不在列表中的元素会被归为同一组,排在所有指定元素之后,它们之间的顺序保持原文档顺序(若需要额外排序,可添加第三层
<xsl:sort>,比如按Child/FieldValue字符串排序)
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

