如何让带命名空间前缀的XML在XSLT缩进转换中正常工作?
解决带命名空间前缀的XML缩进问题(基于MSXML)
核心问题根源
用createElement带前缀创建元素时,MSXML未正确绑定命名空间URI,导致内存中的DOM节点处于前缀无对应URI的无效状态,直接调用transformNodeToObject转换时触发命名空间错误。存盘再重新加载的过程会让MSXML重新解析并修复节点的命名空间绑定,因此能正常转换。
正确实现方案
1. 创建元素时严格绑定命名空间URI
不要直接使用createElement("prefix:elementName"),改用带命名空间的createNode方法:
// 示例:创建UBL发票的cac:Invoice元素 var nsUri = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"; var invoiceElem = xmlDoc.createNode(1, "Invoice", nsUri); // 手动设置前缀(与目标命名空间前缀保持一致) invoiceElem.prefix = "cac";
这种方式创建的节点会同时包含前缀、本地名和对应的命名空间URI,内存中DOM结构合法,避免后续转换报错。
2. 适配带命名空间的XSLT身份转换
确保XSLT声明所有需要保留的命名空间,且模板匹配使用命名空间限定的节点规则:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- 匹配所有元素,保留原命名空间前缀 --> <xsl:template match="*"> <xsl:element name="{name()}" namespace="{namespace-uri()}"> <xsl:copy-of select="@*"/> <xsl:apply-templates/> </xsl:element> </xsl:template> <!-- 保留文本、注释、处理指令节点 --> <xsl:template match="text()|comment()|processing-instruction()"> <xsl:copy/> </xsl:template> </xsl:stylesheet>
通过{name()}直接保留原节点的前缀+本地名,{namespace-uri()}绑定正确的命名空间URI,确保转换后前缀与命名空间的对应关系不变。
3. 直接转换内存中的DOM节点
完成元素的正确创建和XSLT配置后,直接调用transformNodeToObject即可完成缩进,无需存盘再加载:
var xsltDoc = new ActiveXObject("Microsoft.XMLDOM"); xsltDoc.async = false; xsltDoc.loadXML(xsltContent); // xsltContent为上述XSLT字符串 var outputDoc = new ActiveXObject("Microsoft.XMLDOM"); outputDoc.async = false; xmlDoc.transformNodeToObject(xsltDoc, outputDoc); // 保存缩进后的XML文件 outputDoc.save("indented_invoice.xml");
关键注意事项
- 所有带前缀的元素必须在创建时绑定对应的命名空间URI,不能仅指定前缀。
- XSLT中必须声明原XML中用到的所有命名空间,否则转换时会丢失前缀或触发错误。
- 统一使用
createNode方法创建带命名空间的元素节点,避免使用createElement。
内容的提问来源于stack exchange,提问作者Iori_Yagami
相关产品推荐
相关产品推荐

