Xalan-J:在扩展函数中解析QName文本值的命名空间
解决Xalan-J扩展函数中获取命名空间上下文的问题
你的问题核心在于默认情况下Xalan的DOM解析器未启用命名空间感知,且直接使用DOM节点的lookupNamespaceURI无法正确访问当前转换上下文的命名空间声明——因为Xalan在处理节点时,命名空间信息不会以普通属性的形式存在,而是保存在XPath的命名空间上下文中。
下面是具体的修改方案,分两步解决:
1. 启用TransformerFactory的命名空间感知特性
首先,在创建TransformerFactory后,必须开启命名空间相关的特性,确保解析器能保留文档中的命名空间信息:
public static void main(String[] args) throws TransformerConfigurationException, TransformerException { // ... 原有xslt和document字符串定义 ... TransformerFactory xalanTransFact = new org.apache.xalan.processor.TransformerFactoryImpl(); // 启用命名空间感知和前缀保留 try { xalanTransFact.setFeature("http://xml.org/sax/features/namespaces", true); xalanTransFact.setFeature("http://xml.org/sax/features/namespace-prefixes", true); // 可选:开启安全处理特性 xalanTransFact.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); } catch (TransformerConfigurationException e) { e.printStackTrace(); } // ... 原有模板和转换代码 ... }
2. 借助Xalan的ExpressionContext获取命名空间上下文
不要依赖DOM节点的lookupNamespaceURI,而是通过Xalan提供的ExpressionContext获取内部的XPathContext,它能正确访问当前转换上下文的所有命名空间声明:
修改你的扩展函数实现:
package org.stackoverflow.example; import java.io.*; import javax.xml.transform.*; import javax.xml.transform.stream.*; import org.apache.xalan.extensions.ExpressionContext; import org.apache.xalan.extensions.ExpressionContextImpl; // Xalan内部实现类 import org.apache.xpath.XPathContext; import org.apache.xpath.namespaces.NamespaceSupport; import org.w3c.dom.Node; import org.w3c.dom.traversal.NodeIterator; public class XPathFunctionQNameTextValueResolver { public static final boolean resolveQNameTextValue(ExpressionContext ctx, NodeIterator nodes) { // 从ExpressionContext获取XPath上下文,进而拿到命名空间支持类 XPathContext xpathCtx = ((ExpressionContextImpl) ctx).getXPathContext(); NamespaceSupport nsSupport = xpathCtx.getNamespaceContext(); Node node = nodes.nextNode(); while (node != null) { if (node.hasChildNodes()) { String textValue = node.getFirstChild().getNodeValue(); String[] pfxAndName = textValue.split(":"); String prefix = ""; String name = textValue; if (pfxAndName.length == 2) { prefix = pfxAndName[0]; name = pfxAndName[1]; } // 使用Xalan的NamespaceSupport解析前缀对应的命名空间 String namespace = nsSupport.getNamespaceForPrefix(prefix); System.out.println( String.format( "namespace: %s, prefix:%s, local-name: %s", namespace, prefix, name)); } node = nodes.nextNode(); } return false; } // ... 原有main方法(已添加命名空间特性设置) ... }
为什么原来的方法无效?
- 未启用命名空间感知时,DOM解析器会忽略文档中的命名空间声明,导致
node.lookupNamespaceURI无法找到对应前缀的命名空间。 - DOM Level 2及以上标准中,命名空间声明(
xmlns:*)不属于普通属性,所以你在NamedNodeMap中看不到这些属性,必须通过命名空间专用API访问。
经过上述修改后,你的扩展函数就能正确解析value:some-value对应的命名空间org.stackoverflow.example.value了。
内容的提问来源于stack exchange,提问作者predi
相关产品推荐
相关产品推荐

