XSLT 3.0使用xsl:number统计嵌套crewDrillStep下para编号异常问题
我正在尝试为<para>标签添加编号,每个<crewDrillStep>下仅包含一个<para>,<crewDrillStep>本身支持嵌套。原有逻辑在普通场景下运行正常,但只要存在<if>块,编号就会异常连续计数,移除<if>后编号就能正常生成。
我参考Martin的建议更新了示例,目前效果已经非常接近需求,但业务场景中可能存在多个<crewDrill>,且部分场景下二级<para>的编号不需要重启。此外XML中部分带项目符号的para不需要参与编号。
给定XML结构
<crew> <crewRefCard> <title>Expanded Self-Test Procedures </title> <crewDrill> <if> <caseCond>After program stops at test No. 1:</caseCond> <crewDrillStep id="d1e21189"> <para>RESET switch - ELEC.</para> </crewDrillStep> <crewDrillStep id="d1e21195"> <para>Flip red switches:</para> <crewDrillStep id="d1e21200"> <para>Right.</para> </crewDrillStep> <crewDrillStep id="d1e21206"> <para>Left.</para> </crewDrillStep> <crewDrillStep id="d1e21212"> <para>Rudder.</para> </crewDrillStep> </crewDrillStep> <crewDrillStep id="d1e21219"> <para>Flip orange switch.</para> <crewDrillStep id="d1e21224"> <para>Test No. advances to 2.</para> </crewDrillStep> <crewDrillStep id="d1e21230"> <para>Yellow caution light - On.</para> </crewDrillStep> <crewDrillStep id="d1e21236"> <para>Red warning light - On.</para> </crewDrillStep> <crewDrillStep id="d1e21242"> <para>Program stops.</para> </crewDrillStep> </crewDrillStep> </crewDrill> </crewRefCard> </crew>
所用XSLT模板代码
<xsl:template name="para" match="para | notePara | warningAndCautionPara | attentionListItemPara"> <fo:block text-align="justify"> <xsl:choose> <xsl:when test="self::para and (parent::crewDrillStep)"> <fo:block> <xsl:choose> <xsl:when test="count(ancestor::crewDrillStep)=1"> <xsl:number from="crewDrill" level="any" count="crewDrillStep"/> </xsl:when> <xsl:when test="count(ancestor::crewDrillStep)=2 "> <xsl:attribute name="margin-left" select="'.25in'"/> <xsl:number from="crewDrill/crewDrillStep" level="any" count="para[count(ancestor::crewDrillStep) = 2][not(starts-with(normalize-space(.),'•'))]" format="a. "/> </xsl:when> <xsl:when test="count(ancestor::crewDrillStep)=3"> <xsl:attribute name="margin-left" select="'.50in'"/> <xsl:number from="crewDrill" count="crewDrillStep" format="(1) "/> </xsl:when> <xsl:otherwise/> </xsl:choose> </fo:block> <fo:block> <xsl:apply-templates select="node()" mode="include"/> </fo:block> </xsl:when> </xsl:choose> </fo:block> </xsl:template>
期望输出效果
Expanded Self-Test Procedures After program stops at test No. 1: 1. RESET switch - ELEC. 2. Flip red switches: a. Right b. Left. c. Rudder. 3. Flip orange switch. a. Test No. advances to 2. b. Yellow caution light - On. c. Red warning light - On. d. Program stops.
实际输出效果
Expanded Self-Test Procedures After program stops at test No. 1: 1. RESET switch - ELEC. 2. Flip red switches: a. Right b. Left. c. Rudder. 3. Flip orange switch. d. Test No. advances to 2. e. Yellow caution light - On. f. Red warning light - On. g. Program stops.
问题
如何调整xsl:number配置,实现预期的层级编号效果?
问题核心是二级编号的from属性匹配规则和实际XML结构不符:
原配置中二级para的from="crewDrill/crewDrillStep"要求匹配crewDrill的直接子节点crewDrillStep,但你的XML中crewDrill的直接子节点是<if>,没有符合要求的节点,导致xsl:number默认从文档根节点开始统计所有符合条件的二级para,所以第二个一级步骤下的二级编号就从d开始连续计数了。
调整方案
只需要修改二级para对应的xsl:number配置,将from属性改为匹配当前para所属的一级crewDrillStep即可,修改后的完整代码如下:
<xsl:template name="para" match="para | notePara | warningAndCautionPara | attentionListItemPara"> <fo:block text-align="justify"> <xsl:choose> <xsl:when test="self::para and (parent::crewDrillStep)"> <fo:block> <xsl:choose> <xsl:when test="count(ancestor::crewDrillStep)=1"> <xsl:number from="crewDrill" level="any" count="crewDrillStep"/> </xsl:when> <xsl:when test="count(ancestor::crewDrillStep)=2 "> <xsl:attribute name="margin-left" select="'.25in'"/> <!-- 仅修改这一行的from属性 --> <xsl:number from="ancestor::crewDrillStep[count(ancestor::crewDrillStep) = 1]" level="any" count="para[count(ancestor::crewDrillStep) = 2][not(starts-with(normalize-space(.),'•'))]" format="a. "/> </xsl:when> <xsl:when test="count(ancestor::crewDrillStep)=3"> <xsl:attribute name="margin-left" select="'.50in'"/> <xsl:number from="crewDrill" count="crewDrillStep" format="(1) "/> </xsl:when> <xsl:otherwise/> </xsl:choose> </fo:block> <fo:block> <xsl:apply-templates select="node()" mode="include"/> </fo:block> </xsl:when> </xsl:choose> </fo:block> </xsl:template>
调整说明
修改后的from="ancestor::crewDrillStep[count(ancestor::crewDrillStep) = 1]"会向上查找当前para所属的最顶层一级crewDrillStep,统计范围限定在这个一级步骤的内部,每个一级步骤下的二级para都会重新从a开始计数,完全匹配预期效果。如果部分场景下不需要二级编号重启,对应场景保留原from配置即可。
内容的提问来源于stack exchange,提问作者Caroline

