
本文介绍如何利用 xslt 3 的高级文本处理能力(配合 python 的 `saxonche` 库),精准定位 xml `
` 标签内嵌套 `` 等子元素时的软件名称,并基于 json 提供的上下文与偏移信息,将其安全包裹在 `
在科研文献或技术文档的 XML 标注任务中,常需从混合内容(mixed content)的 <p> 段落中识别特定软件名称(如 MODELLER、PROSA),并为其添加语义化标签(如 <software>)。难点在于:这些名称往往夹杂在纯文本与内联子元素(如 <ref>、<em>)之间,传统正则替换或简单 DOM 遍历极易误伤结构或错位匹配。
推荐方案是采用 XSLT 3 + SaxonC —— 这一组合支持原生 JSON 解析、高阶迭代(xsl:iterate)、文本分析(analyze-string)及隧道参数(tunnel parameters),能完美应对“上下文驱动”的精准定位需求。
以下是一个可直接运行的完整流程:
✅ 步骤 1:安装依赖
pip install saxonche
✅ 步骤 2:准备 XSLT 脚本(wrap-software.xsl)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<!-- 主逻辑:对每个 <p>,遍历 JSON 中所有 software 条目 -->
<xsl:template match="p">
<xsl:iterate select="$json-doc?*[?type = 'software']">
<xsl:param name="p" select="."/>
<xsl:on-completion select="$p"/>
<!-- 仅当当前 <p> 文本包含该软件的 context 时才处理 -->
<xsl:if test="contains($p, ?context)">
<xsl:variable name="transformed-p" as="element(p)">
<xsl:apply-templates select="$p" mode="process">
<xsl:with-param name="software" select="." tunnel="yes"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:next-iteration>
<xsl:with-param name="p" select="$transformed-p"/>
</xsl:next-iteration>
</xsl:if>
</xsl:iterate>
</xsl:template>
<!-- 定义处理模式:仅处理文本节点 -->
<xsl:mode name="process" on-no-match="shallow-copy"/>
<!-- 对 <p> 内任意文本节点进行上下文校验与包装 -->
<xsl:template mode="process" match="p//text()">
<xsl:param name="software" tunnel="yes" as="map(*)"/>
<xsl:choose>
<!-- 关键条件:当前文本必须同时满足:
1) 属于该 software 的 context 子串;
2) 包含其 normalizedForm(如 "MODELLER") -->
<xsl:when test="contains($software?context, .) and contains(., $software?software-name?normalizedForm)">
<xsl:apply-templates
select="analyze-string(., $software?software-name?normalizedForm)"
mode="wrap"/>
</xsl:when>
<xsl:otherwise>
<xsl:next-match/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- 将匹配到的子串包装为 <software> -->
<xsl:template mode="wrap" match="*:match">
<software>{.}</software>
</xsl:template>
<!-- 输出配置 -->
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<!-- 加载 JSON 数据(支持文件读取或内联) -->
<xsl:param name="json-doc" select="json-doc('software-mentions.json')"/>
</xsl:stylesheet>? 说明:json-doc('software-mentions.json') 会自动解析 JSON 文件;若需调试,也可将 JSON 内联为 <xsl:param name="json-data">[...]</xsl:param> 并改用 parse-json($json-data)。
✅ 步骤 3:Python 调用示例
from saxonche import PySaxonProcessor
# 输入 XML 和 XSLT 路径
xml_file = "input.xml"
xslt_file = "wrap-software.xsl"
with PySaxonProcessor() as proc:
xslt_proc = proc.new_xslt30_processor()
# 执行转换
output = xslt_proc.transform_to_string(
source_file=xml_file,
stylesheet_file=xslt_file
)
print(output)⚠️ 注意事项
- 上下文匹配优先级:XSLT 先验证整个 <p> 是否包含 context 字符串,再在匹配的文本节点中查找 normalizedForm,确保不跨句误包(例如避免将 MODEL 错包为 MODELLER 的子串)。
- 嵌套安全:xsl:iterate 逐个应用软件规则,且 mode="process" 严格作用于 text() 节点,完全保留 <ref>、<em> 等子元素结构。
- 大小写敏感:默认区分大小写;如需忽略,可将 contains() 替换为 matches(., concat('\b', $software?software-name?normalizedForm, '\b'), 'i')。
- 性能提示:对于超大 XML,建议预筛选 <p> 标签(如先用 lxml 提取含关键词的段落再交由 XSLT 处理)。
该方法以声明式逻辑替代脆弱的手动 DOM 操作,兼具准确性、可维护性与扩展性——未来新增软件只需更新 JSON,无需修改代码。










