XSLT中w元素xml:id生成异常问题求助
问题:XSLT 3.0中w元素的xml:id无法正确继承父元素l的xml:id
我使用的样式表版本是version="3.0",引擎为Saxon PEE 9.9.1.7。需求是让w元素的xml:id由父元素l的xml:id + '_' + w的text()值组成,但实际转换结果不符合预期。
转换前XML示例
<div3 xml:id="ktu1-1_ii"> <!-- tei elements div4 and lg --> <l n="1a"> <!-- other children --> <w>l</w> </l> </div3>
现有XSLT模板代码
<xsl:template match="div3/div4/lg/l"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:if test="not(@xml:id)"> <xsl:attribute name="xml:id"> <xsl:value-of select="concat(ancestor::div3/@xml:id, '_l', @n)"/> </xsl:attribute> </xsl:if> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="l/w"> <xsl:variable name="xmlid"> <xsl:value-of select="ancestor::l/@xml:id"/> </xsl:variable> <xsl:message select="$xmlid"/> <!-- test @xml:id, nothing --> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:attribute name="pos"></attribute> <!-- Calculate xml:id based on parent 'l' xml:id --> <xsl:attribute name="xml:id"> <xsl:value-of select="normalize-space(replace(ancestor::l/@xml:id, ' ', '')) || '_' || replace(., '\s|̊', '')"/> </xsl:attribute> <xsl:apply-templates/> </xsl:copy> </xsl:template>
转换结果
l元素结果正确:<l n='1a' xml:id='ktu1-1_ii_l1a'>w元素结果不符合预期:<w xml:id="_l">l</w>,预期应为<w xml:id="ktu1-1_ii_l1a_l">l</w>
错误原因与解决方案
核心问题
XSLT处理节点时,当前节点的上下文是原始XML文档中的节点,而非模板处理后生成的新节点。你在l的模板中动态添加了xml:id属性,但在处理w元素时,ancestor::l/@xml:id访问的是原始XML中l元素的属性——而原始XML里的l并没有xml:id,所以返回空值,最终拼接出_l。
解决方案
有两种可行的处理方式:
方式一:提前计算l的xml:id并传递给子节点
修改l的模板,将计算好的xml:id存储为变量,在调用apply-templates时通过xsl:with-param传递给w的模板:
<xsl:template match="div3/div4/lg/l"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:variable name="l-xml-id"> <xsl:choose> <xsl:when test="@xml:id"> <xsl:value-of select="@xml:id"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat(ancestor::div3/@xml:id, '_l', @n)"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:if test="not(@xml:id)"> <xsl:attribute name="xml:id" select="$l-xml-id"/> </xsl:if> <!-- 传递变量给子节点模板 --> <xsl:apply-templates> <xsl:with-param name="parent-l-id" select="$l-xml-id"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="l/w"> <xsl:param name="parent-l-id"/> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:attribute name="pos"></attribute> <xsl:attribute name="xml:id" select="concat($parent-l-id, '_', replace(., '\s|̊', ''))"/> <xsl:apply-templates/> </xsl:copy> </xsl:template>
方式二:使用隧道参数(Tunnel Parameter)
如果w元素嵌套较深,使用隧道参数可以避免逐层传递变量:
<xsl:template match="div3/div4/lg/l"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:variable name="l-xml-id"> <xsl:choose> <xsl:when test="@xml:id"> <xsl:value-of select="@xml:id"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat(ancestor::div3/@xml:id, '_l', @n)"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:if test="not(@xml:id)"> <xsl:attribute name="xml:id" select="$l-xml-id"/> </xsl:if> <!-- 定义隧道参数 --> <xsl:apply-templates> <xsl:with-param name="parent-l-id" select="$l-xml-id" tunnel="yes"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="l/w"> <!-- 接收隧道参数 --> <xsl:param name="parent-l-id" tunnel="yes"/> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:attribute name="pos"></attribute> <xsl:attribute name="xml:id" select="concat($parent-l-id, '_', replace(., '\s|̊', ''))"/> <xsl:apply-templates/> </xsl:copy> </xsl:template>
补充说明
- 两种方式都直接复用了
l元素模板中计算好的xml:id值,避免了依赖原始节点的属性。 - 使用
select属性直接赋值给xsl:attribute,比嵌套xsl:value-of更简洁高效,符合XSLT 3.0的最佳实践。
内容的提问来源于stack exchange,提问作者Vanessa
相关产品推荐
相关产品推荐

