XSLT复制节点丢失祖先已定义的xmlns属性,如何保留冗余属性?
问题:保留XSLT转换中后代节点的冗余xmlns属性
使用XSLT复制节点和属性时,当xmlns属性已由祖先节点定义,复制后代节点时这些属性会丢失。从功能上看,后代节点的这类属性定义属于冗余(若理解有误请指正),推测这是属性未被写入输出的原因,但没找到相关依据。请问有没有办法保留后代节点上这些重复/冗余的xmlns属性?
操作环境
- openjdk 11.0.19 2023-04-18
- Ubuntu 22.04
- Saxon HE 11.5
使用的XSLT(标识转换)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
示例输入XML
祖先节点<OAI-PMH>与后代节点<oai_qdc:qualifieddc>存在4个重复的xmlns属性:
<?xml version="1.0" encoding="UTF-8"?> <OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oai_qdc="http://worldcat.org/xmlschemas/qdc-1.0/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dc="http://purl.org/dc/elements/1.1/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd"> <ListRecords> <record> <header> <datestamp>1998-07-05T07:00:00Z</datestamp> </header> <metadata> <oai_qdc:qualifieddc xmlns:oai_qdc="http://worldcat.org/xmlschemas/qdc-1.0/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://worldcat.org/xmlschemas/qdc-1.0/ http://worldcat.org/xmlschemas/qdc-1.0/qdc-1.0.xsd"> <dc:title>Some Title</dc:title> </oai_qdc:qualifieddc> </metadata> </record> </ListRecords> </OAI-PMH>
转换后输出XML(冗余xmlns属性丢失)
<?xml version="1.0" encoding="UTF-8"?> <OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:oai_qdc="http://worldcat.org/xmlschemas/qdc-1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd"> <ListRecords> <record> <header> <datestamp>1998-07-05T07:00:00Z</datestamp> </header> <metadata> <oai_qdc:qualifieddc xsi:schemaLocation="http://worldcat.org/xmlschemas/qdc-1.0/ http://worldcat.org/xmlschemas/qdc-1.0/qdc-1.0.xsd"> <dc:title>Some Title</dc:title> </oai_qdc:qualifieddc> </metadata> </record> </ListRecords> </OAI-PMH>
解答
原因说明
你说得没错,后代节点上重复的xmlns属性确实属于冗余——XML的命名空间作用域是从声明节点向下传递的,后代节点继承了祖先的命名空间声明,所以这些重复声明在语义上完全多余。XSLT处理器(比如Saxon)在序列化输出时会自动优化掉这类冗余声明,这是符合XML规范的行为。
解决方案
如果必须保留这些冗余的xmlns属性,可以通过以下两种方式实现:
方法1:使用Saxon扩展序列化参数
Saxon提供了扩展参数saxon:suppress-redundant-namespaces,将其设置为no即可强制保留所有命名空间声明,包括冗余的。修改XSLT如下:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://saxon.sf.net/"> <xsl:output method="xml" indent="yes" saxon:suppress-redundant-namespaces="no"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
注意需要将XSLT版本指定为2.0+,并声明Saxon的命名空间。
方法2:显式复制命名空间属性
如果需要兼容更多处理器,可以显式匹配命名空间节点,并将其转换为普通的xmlns属性输出。修改模板如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="@*|node()"> <xsl:copy> <!-- 显式复制所有命名空间节点为xmlns属性 --> <xsl:for-each select="namespace::*"> <xsl:attribute name="xmlns:{local-name()}" namespace=""> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 避免重复处理已转换的命名空间属性 --> <xsl:template match="@xmlns:*"/> </xsl:stylesheet>
这个方法通过遍历节点的命名空间节点,手动创建对应的xmlns属性,同时忽略原本的xmlns属性(避免重复),从而强制保留所有层级的命名空间声明。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

