XSL代码无法对SEC_Match元素按STARTEFFDATE降序排序求助
解决XSL中SEC元素转SEC_Match并按STARTEFFDATE降序排序的问题
需求概述
需将XML中**具有相同UID前缀(^符号前的部分)**的SEC元素转换为SEC_Match元素,且所有SEC_Match元素按@STARTEFFDATE属性值降序排列。
原XSL代码
<xsl:template match="*[SEC]"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:for-each select="*"> <xsl:variable name="a" select="substring-before(@UID,'^')"/> <xsl:choose> <xsl:when test="self::SEC[$a = substring-before(following-sibling::SEC[1]/@UID,'^')]|self::SEC[$a = substring-before(preceding-sibling::SEC[1]/@UID,'^')]"> <xsl:for-each select="self::SEC[$a = substring-before(following-sibling::SEC[1]/@UID,'^')]|self::SEC[$a = substring-before(preceding-sibling::SEC[1]/@UID,'^')]"> <xsl:sort select="@STARTEFFDATE" data-type="number" order="descending"/> <SEC_Match> <xsl:apply-templates select="@*"/> <xsl:apply-templates/> </SEC_Match> </xsl:for-each> </xsl:when> <xsl:otherwise> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:copy> </xsl:template>
输入XML
<LEVEL2 UID="XXXX" CAPTION="CH" D="381" PRIOR="NO" MEDIA="ALL"> <NAME>Public Health: </NAME> <SEC D="3-4-218" UID="ar3-4-218^XXXX" STARTEFFDATE="20171001"> <NAME> permitted premises</NAME> <SBSEC D="(a)" LOC="3-4-218(a)" ANCHOR="3-4-218(a)" PERIOD="0" PRIOR="NO"> <P> test test </P> </SBSEC> </SEC> <SEC D="381.986" UID="fl381.986^XXXXX" STARTEFFDATE="600" > <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P>who has agreed to assist</P> </SBSEC> </SEC> <SEC D="381.986" UID="fl381.986^XXXX" STARTEFFDATE="200"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P> “Attractive to children”</P> </SBSEC> </SEC> <SEC D="381.986" UID="fl381.986^XXXX" STARTEFFDATE="800"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P> “Attractive to children” means the use of any image or words</P> </SBSEC> </SEC> <SEC D="3-4-218" UID="ar3-4-218^XXXXX" STARTEFFDATE="20171001"> <NAME>Permits restricted to permitted premises</NAME> <SBSEC D="(a)" LOC="3-4-218(a)" ANCHOR="3-4-218(a)" PERIOD="0" PRIOR="NO"> <P>goods, dry goods, hardware, or general mercantile store. </P> </SBSEC> </SEC> <SEC D="381.986" UID="fl381.986^XXXXX" STARTEFFDATE="100"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P>identification card, and meets the requirements of subsection (6). </P> </SBSEC> </SEC> </LEVEL2>
当前输出XML
<LEVEL2 UID="XXXX" CAPTION="CH" D="381" PRIOR="NO" MEDIA="ALL"> <NAME>Public Health: </NAME> <SEC D="3-4-218" UID="ar3-4-218^XXXX" STARTEFFDATE="20171001"> <NAME> permitted premises</NAME> <SBSEC D="(a)" LOC="3-4-218(a)" ANCHOR="3-4-218(a)" PERIOD="0" PRIOR="NO"> <P> test test </P> </SBSEC> </SEC> <SEC_Match D="381.986" UID="fl381.986^XXXXX" STARTEFFDATE="600"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P>who has agreed to assist</P> </SBSEC> </SEC_Match> <SEC_Match D="381.986" UID="fl381.986^XXXX" STARTEFFDATE="200"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P> “Attractive to children”</P> </SBSEC> </SEC_Match> <SEC_Match D="381.986" UID="fl381.986^XXXX" STARTEFFDATE="800"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P> “Attractive to children” means the use of any image or words</P> </SBSEC> </SEC_Match> <SEC D="3-4-218" UID="ar3-4-218^XXXXX" STARTEFFDATE="20171001"> <NAME>Permits restricted to permitted premises</NAME> <SBSEC D="(a)" LOC="3-4-218(a)" ANCHOR="3-4-218(a)" PERIOD="0" PRIOR="NO"> <P>goods, dry goods, hardware, or general mercantile store. </P> </SBSEC> </SEC> <SEC D="381.986" UID="fl381.986^XXXXX" STARTEFFDATE="100"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P>identification card, and meets the requirements of subsection (6). </P> </SBSEC> </SEC> </LEVEL2>
问题分析
原代码存在以下缺陷:
- 分组逻辑错误:仅通过相邻节点判断是否属于同组,导致同UID前缀但不相邻的SEC被遗漏(如最后一个STARTEFFDATE=100的SEC未被转换为SEC_Match)。
- 排序范围过小:内部
xsl:for-each仅选择当前节点及单个相邻匹配节点,无法覆盖同组所有SEC,排序仅对局部节点生效。 - 遍历方式低效:逐个处理子节点,重复判断分组,容易出现遗漏和重复输出。
修正后的XSL代码(XSLT 1.0)
采用Muenchian分组实现按UID前缀分组,再对每组内节点排序:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 定义键:按UID前缀分组 --> <xsl:key name="secByUidPrefix" match="SEC" use="substring-before(@UID, '^')"/> <!-- 复制所有节点的默认模板 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 匹配包含SEC的父节点(如LEVEL2) --> <xsl:template match="*[SEC]"> <xsl:copy> <xsl:apply-templates select="@*"/> <!-- 先处理非SEC节点 --> <xsl:apply-templates select="*[not(self::SEC)]"/> <!-- 处理SEC分组:仅选择每组的第一个节点作为代表 --> <xsl:for-each select="SEC[generate-id() = generate-id(key('secByUidPrefix', substring-before(@UID, '^'))[1])]"> <xsl:variable name="prefix" select="substring-before(@UID, '^')"/> <!-- 遍历同组所有SEC,按STARTEFFDATE降序排序 --> <xsl:for-each select="key('secByUidPrefix', $prefix)"> <xsl:sort select="@STARTEFFDATE" data-type="number" order="descending"/> <SEC_Match> <xsl:apply-templates select="@*|node()"/> </SEC_Match> </xsl:for-each> </xsl:for-each> </xsl:copy> </xsl:template> </xsl:stylesheet>
期望输出XML
<LEVEL2 UID="XXXX" CAPTION="CH" D="381" PRIOR="NO" MEDIA="ALL"> <NAME>Public Health: </NAME> <SEC_Match D="3-4-218" UID="ar3-4-218^XXXX" STARTEFFDATE="20171001"> <NAME> permitted premises</NAME> <SBSEC D="(a)" LOC="3-4-218(a)" ANCHOR="3-4-218(a)" PERIOD="0" PRIOR="NO"> <P> test test </P> </SBSEC> </SEC_Match> <SEC_Match D="3-4-218" UID="ar3-4-218^XXXXX" STARTEFFDATE="20171001"> <NAME>Permits restricted to permitted premises</NAME> <SBSEC D="(a)" LOC="3-4-218(a)" ANCHOR="3-4-218(a)" PERIOD="0" PRIOR="NO"> <P>goods, dry goods, hardware, or general mercantile store. </P> </SBSEC> </SEC_Match> <SEC_Match D="381.986" UID="fl381.986^XXXX" STARTEFFDATE="800"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P> “Attractive to children” means the use of any image or words</P> </SBSEC> </SEC_Match> <SEC_Match D="381.986" UID="fl381.986^XXXXX" STARTEFFDATE="600"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P>who has agreed to assist</P> </SBSEC> </SEC_Match> <SEC_Match D="381.986" UID="fl381.986^XXXX" STARTEFFDATE="200"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P> “Attractive to children”</P> </SBSEC> </SEC_Match> <SEC_Match D="381.986" UID="fl381.986^XXXXX" STARTEFFDATE="100"> <NAME>Medical use of marijuana</NAME> <SBSEC D="(1)" LOC="381.986(1)" ANCHOR="381.986(1)" PERIOD="0" PRIOR="NO"> <P>identification card, and meets the requirements of subsection (6). </P> </SBSEC> </SEC_Match> </LEVEL2>
内容的提问来源于stack exchange,提问作者Amrendra Kumar
相关产品推荐
相关产品推荐

