如何正确使用XPath following-sibling轴解决TEI XML转换问题
问题:TEI XML中转时关联对应的XSLT修正
需要将TEI格式的<desc>元素转换为每个<placeName>对应的<event>元素,但当前实现存在两个问题:要么所有<note>都被附加到每个<event>中,要么完全无法获取到与<placeName>对应的<note>。
原TEI XML结构
<desc year="1879"> <date from="1879-08-30" to="1879-08-30"/> <placeName>New York</placeName> <placeName>New Jersey</placeName> <note>visiting my grandma</note> <date from="1879-10-30" to="1879-11-01"/> <placeName>Berlin</placeName> <note>with my mother</note> <placeName>Hamburg</placeName> </desc>
原XSLT代码(存在问题)
<xsl:template match="//tei:desc"> <xsl:variable name="note-content"> <xsl:for-each select="//tei:placeName"> <xsl:if test="following-sibling::tei:note[1]"> <xsl:element name="note" namespace="http://www.tei-c.org/ns/1.0"> <xsl:value-of select="following-sibling::tei:note[1]"/> </xsl:element> </xsl:if> </xsl:for-each> </xsl:variable> <xsl:for-each-group select="*" group-starting-with="tei:date"> <!-- other specifications --> <xsl:for-each select="current-group()[name()='placeName']"> <xsl:element name="event" namespace="http://www.tei-c.org/ns/1.0"> <!-- other specifications --> <xsl:element name="desc" namespace="http://www.tei-c.org/ns/1.0"> <xsl:element name="placeName" namespace="http://www.tei-c.org/ns/1.0"> <xsl:attribute name="ref"/> <xsl:value-of select="."/> </xsl:element> </xsl:element> <xsl:if test="$note-content !=''"> <xsl:element name="note" namespace="http://www.tei-c.org/ns/1.0"> <xsl:value-of select="$note-content"/> </xsl:element> </xsl:if> </xsl:element> </xsl:for-each> </xsl:for-each-group> </xsl:template>
解决方案:精准关联<placeName>与对应<note>
原代码的核心问题是提前全局收集了所有<note>内容,导致无法按分组和节点顺序匹配。正确的做法是在分组内动态判断每个<placeName>对应的<note>,利用following-sibling轴结合分组上下文实现精准匹配。
修改后的XSLT代码
<xsl:template match="tei:desc"> <xsl:for-each-group select="*" group-starting-with="tei:date"> <!-- 先获取当前date分组内的第一个note --> <xsl:variable name="current-group-note" select="current-group()[self::tei:note][1]"/> <xsl:for-each select="current-group()[self::tei:placeName]"> <event xmlns="http://www.tei-c.org/ns/1.0"> <!-- 保留原有的desc和placeName结构 --> <desc> <placeName ref=""> <xsl:value-of select="."/> </placeName> </desc> <!-- 两个判断条件:要么是分组内最后一个placeName,要么后面紧跟的元素是note --> <xsl:if test="position() = last() or following-sibling::*[1][self::tei:note]"> <note> <xsl:value-of select="$current-group-note"/> </note> </xsl:if> </event> </xsl:for-each> </xsl:for-each-group> </xsl:template>
关键逻辑说明
- 分组内提取note:通过
current-group()[self::tei:note][1]获取当前date分组下的第一个<note>,确保只处理当前时间段内的备注。 - 匹配对应placeName:
- 如果当前
<placeName>是分组内最后一个,则关联该分组的<note>(如原XML中的New Jersey、Hamburg); - 如果当前
<placeName>后面直接紧跟<note>,则关联该<note>(如原XML中的Berlin)。
- 如果当前
- 去掉全局note变量:避免提前收集所有note导致的错误关联,改为在分组内动态处理。
转换后预期结果
- New York、New Jersey 对应的
<event>都会附加note: visiting my grandma; - Berlin、Hamburg 对应的
<event>都会附加note: with my mother。
内容的提问来源于stack exchange,提问作者Laura P
相关产品推荐
相关产品推荐

