如何用XSLT 3.0为XML文献引用添加交叉链接?
使用XSLT 3.0为文献引用添加交叉链接
针对你需要给XML中带/不带et al.的文献引用自动添加交叉链接的需求,这里提供一个基于XSLT 3.0的实现方案,核心思路是先预生成文献的匹配规则,再扫描文本替换匹配的引用为链接元素。
假设的输入输出结构
输入XML示例
<document> <content> <p>参考Smith et al. (2020)的研究,以及Jones (2019)的结论。</p> <p>另外,Doe, Smith and Brown (2021)的成果也很重要。</p> </content> <bibliography> <bib id="smith2020"> <author>Smith, John; Doe, Jane; Brown, Bob</author> <year>2020</year> <title>XSLT 3.0 Advanced Techniques</title> </bib> <bib id="jones2019"> <author>Jones, Alice</author> <year>2019</year> <title>XML Cross-Reference Methods</title> </bib> <bib id="doe2021"> <author>Doe, Jane; Smith, John; Brown, Bob</author> <year>2021</year> <title>Advanced XML Processing</title> </bib> </bibliography> </document>
预期输出XML示例
<document> <content> <p>参考<xref ref="smith2020">Smith et al. (2020)</xref>的研究,以及<xref ref="jones2019">Jones (2019)</xref>的结论。</p> <p>另外,<xref ref="doe2021">Doe, Smith and Brown (2021)</xref>的成果也很重要。</p> </content> <bibliography> <bib id="smith2020"> <author>Smith, John; Doe, Jane; Brown, Bob</author> <year>2020</year> <title>XSLT 3.0 Advanced Techniques</title> </bib> <bib id="jones2019"> <author>Jones, Alice</author> <year>2019</year> <title>XML Cross-Reference Methods</title> </bib> <bib id="doe2021"> <author>Doe, Jane; Smith, John; Brown, Bob</author> <year>2021</year> <title>Advanced XML Processing</title> </bib> </bibliography> </document>
XSLT 3.0实现代码
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 默认复制所有未匹配的元素和属性 --> <xsl:mode on-no-match="shallow-copy"/> <!-- 预生成文献引用的匹配规则映射 --> <xsl:variable name="bib-ref-map" as="map(xs:string, map(xs:string, xs:string))"> <xsl:map> <xsl:for-each select="//bib"> <xsl:variable name="authors" select="tokenize(author, '; ')" as="xs:string*"/> <!-- 提取作者姓氏(假设作者格式为 "Surname, Firstname") --> <xsl:variable name="author-surnames" select="for $a in $authors return replace($a, '^(.*?), .*$', '$1')" as="xs:string*"/> <xsl:variable name="year" select="year/text()" as="xs:string"/> <xsl:variable name="bib-id" select="@id" as="xs:string"/> <xsl:map-entry key="$bib-id"> <xsl:map> <!-- 单作者引用规则:姓氏 + (年份) --> <xsl:if test="count($author-surnames) = 1"> <xsl:map-entry key="single" select="concat($author-surnames[1], ' \(', $year, '\)')"/> </xsl:if> <!-- 多作者全名引用规则:姓氏1, 姓氏2 and 姓氏3 + (年份) --> <xsl:if test="count($author-surnames) > 1"> <xsl:map-entry key="full" select="concat( string-join($author-surnames[position() ne last()], ', '), ' and ', $author-surnames[last()], ' \(', $year, '\)' )"/> </xsl:if> <!-- 多作者et al引用规则:第一作者姓氏 + et al. + (年份) --> <xsl:if test="count($author-surnames) > 1"> <xsl:map-entry key="etal" select="concat($author-surnames[1], ' et al\. \(', $year, '\)')"/> </xsl:if> </xsl:map> </xsl:map-entry> </xsl:for-each> </xsl:map> </xsl:variable> <!-- 整理所有匹配规则,按长度倒序排列(优先匹配长格式,避免短规则覆盖长规则) --> <xsl:variable name="ref-patterns" as="element()*"> <xsl:for-each select="map:keys($bib-ref-map)"> <xsl:variable name="bib-rules" select="$bib-ref-map(.)"/> <xsl:for-each select="map:keys($bib-rules)"> <pattern ref-id="{.}" pattern="{$bib-rules(.)}"/> </xsl:for-each> </xsl:for-each> <xsl:sort select="string-length(@pattern)" order="descending"/> </xsl:variable> <!-- 处理文本节点,替换匹配的引用为交叉链接 --> <xsl:template match="text()"> <xsl:param name="remaining-text" select="." as="xs:string"/> <xsl:if test="$remaining-text"> <xsl:variable name="matched-rule" as="element(pattern)?" select="$ref-patterns[matches($remaining-text, concat('^.*?(', @pattern, ')'))]"/> <xsl:choose> <xsl:when test="$matched-rule"> <!-- 输出匹配前的文本 --> <xsl:value-of select="replace($remaining-text, concat('^(.*?)', $matched-rule/@pattern, '.*$'), '$1')"/> <!-- 生成带引用的链接元素 --> <xref ref="{$matched-rule/@ref-id}"> <xsl:value-of select="replace($remaining-text, concat('^.*?(', $matched-rule/@pattern, ').*$'), '$1')"/> </xref> <!-- 递归处理剩余文本 --> <xsl:call-template name="text"> <xsl:with-param name="remaining-text" select="replace($remaining-text, concat('^.*?', $matched-rule/@pattern, '(.*)$'), '$1')"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <!-- 无匹配时直接输出剩余文本 --> <xsl:value-of select="$remaining-text"/> </xsl:otherwise> </xsl:choose> </xsl:if> </xsl:template> </xsl:stylesheet>
关键逻辑说明
- 文献规则映射:遍历所有
<bib>条目,提取作者姓氏和年份,生成三种引用格式的正则匹配规则(单作者、多作者全名、多作者et al.),并存储在映射中方便快速查找。 - 规则排序:将匹配规则按长度倒序排列,确保长格式的全名引用优先被匹配,避免
et al.格式提前匹配导致全名引用被遗漏。 - 文本递归替换:通过递归扫描文本节点,每次找到第一个匹配的引用规则,将其替换为指定的交叉链接元素(这里用
<xref>,你可以根据需求改成<a>或其他元素),然后继续处理剩余文本。 - 正则转义:对正则中的特殊字符(如括号、点号)进行转义,确保匹配的准确性。
适配调整
如果你的XML结构和示例不同,只需做以下调整:
- 若引用文本在特定元素内(如
<ref>),将模板匹配改为match="ref/text()"。 - 若链接元素不是
<xref>,替换代码中的<xref>为你需要的元素名称,并调整对应的属性。 - 若作者格式不是"Surname, Firstname",修改提取姓氏的
replace表达式。
内容的提问来源于stack exchange,提问作者Balaji
相关产品推荐
相关产品推荐

