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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 20:52:31