You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XSLT转换中如何忽略引用的DTD?解决模板匹配异常

问题描述

在Oxygen中使用Saxon运行XSLT 3.0转换时遇到以下问题:

初始XSLT模板(匹配所有元素*):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    expand-text="yes"
    version="3.0">    
    <xsl:output indent="yes" method="xml" omit-xml-declaration="no" encoding="utf-8"/>
    
    <xsl:template match="/">   
        <xsl:text>&#xa;</xsl:text>
        <xsl:apply-templates select="*"/>
    </xsl:template>
    
    <xsl:template match="*">
<!-- On Martin's suggestion I should use node-name instead of name, so I have changed this, but the result is the same. -->
        <xsl:value-of select="node-name()"/><xsl:text>&#xa;</xsl:text>
        <xsl:apply-templates select="*"/>
    </xsl:template>
</xsl:stylesheet>

针对带DTD声明的XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ddn PUBLIC "-//S1000D//DTD Data Dispatch Note 20050501//EN//XML" "http://www.s1000d.org/s1000d_2-2/xml_dtd/ddn/dtd/ddn.dtd">
<ddn>
    <ddnc>
        <modelic>ABC</modelic>
        <sendid>AASSD</sendid>
        <recvid>VVBBN</recvid>
        <diyear>2024</diyear>
        <seqnum>00001</seqnum>
    </ddnc>
</ddn>

转换输出正常,能列出所有元素名称:

<?xml version="1.0" encoding="utf-8"?>
ddn
ddnc
modelic
sendid
recvid
diyear
seqnum

但将模板改为仅匹配ddn元素后:

<xsl:template match="ddn">
     <xsl:value-of select="node-name()"/><xsl:text>&#xa;</xsl:text>
     <xsl:apply-templates select="*"/>
</xsl:template>

输出异常,未打印ddn及子元素名称,仅输出文本内容:

<?xml version="1.0" encoding="utf-8"?>
        ABC
        AASSD
        VVBBN
        2024
        0000

移除XML的DOCTYPE声明后,输出恢复正常;改用match="*:ddn"匹配模板时,输出也恢复正常。

原因分析

问题核心在于DTD将ddn等元素定义到了某个命名空间中,尽管XML文件中没有显式使用命名空间前缀,但解析器加载DTD后,会将元素绑定到对应的命名空间。此时:

  • match="ddn"仅匹配无命名空间的ddn元素,无法命中实际处于命名空间中的目标元素,因此会触发XSLT的默认模板,直接输出元素的文本内容。
  • match="*:ddn"是匹配任意命名空间下的ddn元素,因此能正确命中目标元素,执行自定义模板逻辑。
解决方案

方案1:让Saxon忽略DTD加载

通过禁用DTD加载,让解析器不处理DTD,元素将保持无命名空间状态,此时match="ddn"即可正常匹配:

  • 在Oxygen中:打开转换场景配置,找到Saxon的Parser Options,勾选Disable DTD loading(或类似选项,不同版本表述可能略有差异)。
  • 命令行运行Saxon时:添加参数 -dtd:off,示例命令:
java -jar saxon-he-xxx.jar -s:input.xml -xsl:stylesheet.xsl -o:output.xml -dtd:off

方案2:调整XSLT的命名空间匹配规则

如果需要保留DTD处理,可通过以下方式适配命名空间:

  1. 明确绑定命名空间前缀:先确定DTD定义的命名空间URI(可通过node-name(/*)在XSLT中输出查看),然后在XSLT中声明对应前缀并匹配:
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:math="http://www.w3.org/2005/xpath-functions/math"
        xmlns:s1000d="http://www.s1000d.org/s1000d_2-2" <!-- 替换为实际命名空间URI -->
        exclude-result-prefixes="xs math s1000d"
        expand-text="yes"
        version="3.0">   
    
        <!-- ... 其他代码 ... -->
    
        <xsl:template match="s1000d:ddn"> <!-- 用前缀匹配目标命名空间的元素 -->
            <xsl:value-of select="node-name()"/><xsl:text>&#xa;</xsl:text>
            <xsl:apply-templates select="*"/>
        </xsl:template>
    </xsl:stylesheet>
    
  2. 通用匹配任意命名空间:继续使用match="*:ddn",该方式无需关心具体命名空间URI,可匹配所有命名空间下的ddn元素。

内容的提问来源于stack exchange,提问作者Zug_Bug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 09:04:52