Xalan-J:在扩展函数中解析QName文本值的名称空间

问题描述

我已经实现了一个简单的XPath扩展功能,该功能旨在由Xalan-J在XSLT转换期间调用调用部分很简单,但是我不明白的是,该函数的实现应该如何访问要转换的文档的名称空间上下文(不是转换的上下文,它是扩展函数的第一个参数)。我需要此上下文以便解析实际上是QName的元素文本值的命名空间。

这是一个需要转换的示例文档:

<document xmlns='org.stackoverflow.example.document'>
    <element xmlns:value="org.stackoverflow.example.value">value:some-value</element>
</document>

value:some-value是我要解析的QName文本值的示例-它表示some-value命名空间中的org.stackoverflow.example.value

转换看起来像这样:

<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
                xmlns:sofun='xalan://org.stackoverflow.example.XPathFunctionQNameTextValueResolver'
                xmlns:doc='org.stackoverflow.example.document'>
    <xsl:template match='/doc:document/doc:element'>
        <xsl:value-of select='sofun:resolveQNameTextValue(.)' />
    </xsl:template>
</xsl:stylesheet>

它实际上只是使用节点element作为唯一参数调用扩展函数

因此实际的代码如下所示(在类路径中需要Xalan-J 2.7.1):

package org.stackoverflow.example;

import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import org.apache.xalan.extensions.ExpressionContext;
import org.w3c.dom.NamednodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;

public class XPathFunctionQNameTextValueResolver {
    
    public static final boolean resolveQNameTextValue(ExpressionContext ctx,NodeIterator nodes) {        
        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];
                }
                String namespace = node.lookupNamespaceURI(prefix);
                // "namespace" is always null,unless null is supplied as "prefix",which returns the default namespace
                NamednodeMap attributes = node.getAttributes();
                if (attributes != null) {
                    // "attributes" does not contain any "xmlns" attributes
                }
                System.out.println(
                        String.format(
                                "namespace: %s,prefix:%s,local-name: %s,attributes-len: %d",namespace,prefix,name,attributes != null ? attributes.getLength() : 0));
            }
            node = nodes.nextNode();
        }
        
        return false;
    }
    
    public static void main(String[] args) throws TransformerConfigurationException,TransformerException {
        String xslt = "" +
"<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'\n" +
"                xmlns:sofun='xalan://org.stackoverflow.example.XPathFunctionQNameTextValueResolver'\n" +
"               xmlns:doc='org.stackoverflow.example.document'>\n" +
"   <xsl:template match='/doc:document/doc:element'>\n" +
"       <xsl:value-of select='sofun:resolveQNameTextValue(.)' />\n" +
"   </xsl:template>\n" +
"</xsl:stylesheet>";
        String document = "" +
"<document xmlns='org.stackoverflow.example.document'>\n" +
"   <element xmlns:value=\"org.stackoverflow.example.value\">value:some-value</element>\n" +
"</document>";
        
        TransformerFactory xalanTransFact = new org.apache.xalan.processor.TransformerFactoryImpl();
        Templates template = xalanTransFact.newTemplates(new StreamSource(new StringReader(xslt)));
        
        StringWriter writer = new StringWriter();
        Transformer transformer = template.newTransformer();
        transformer.transform(new StreamSource(new StringReader(document)),new StreamResult(writer));
    }
    
}

期望node.lookupNamespaceURI(prefix)将提供我需要的名称空间,但事实并非如此。 node实际上是DOM模型中的一个节点,但是这些节点似乎不包含任何名称空间信息。就像生成这些节点的解析器配置错误并完全忽略名称空间一样。

如何更改示例,以便在运行时执行XPath函数的实现时出现名称空间信息?

解决方法

根据@MartinHonnen在评论中的建议,如果需要命名空间,则需要自己提供一个了解命名空间的输入文档解析器:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document parse = builder.parse(new InputSource(new StringReader(document)));
transformer.transform(new DOMSource(parse),new StreamResult(writer));

更正的示例:

package org.stackoverflow.example;

import java.io.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.*;
import org.apache.xalan.extensions.ExpressionContext;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class XPathFunctionQNameTextValueResolver {
    
    public static final boolean resolveQNameTextValue(ExpressionContext ctx,NodeIterator nodes) {        
        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];
                }
                String namespace = node.lookupNamespaceURI(prefix);
                // "namespace" is always null,unless null is supplied as "prefix",which returns the default namespace
                NamedNodeMap attributes = node.getAttributes();
                if (attributes != null) {
                    // "attributes" does not contain any "xmlns" attributes
                }
                System.out.println(
                        String.format(
                                "namespace: %s,prefix:%s,local-name: %s,attributes-len: %d",namespace,prefix,name,attributes != null ? attributes.getLength() : 0));
            }
            node = nodes.nextNode();
        }
        
        return false;
    }
    
    public static void main(String[] args) throws TransformerConfigurationException,TransformerException,ParserConfigurationException,IOException,SAXException {
        String xslt = "" +
"<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'\n" +
"                xmlns:sofun='xalan://org.stackoverflow.example.XPathFunctionQNameTextValueResolver'\n" +
"               xmlns:doc='org.stackoverflow.example.document'>\n" +
"   <xsl:template match='/doc:document/doc:element'>\n" +
"       <xsl:value-of select='sofun:resolveQNameTextValue(.)' />\n" +
"   </xsl:template>\n" +
"</xsl:stylesheet>";
        String document = "" +
"<document xmlns='org.stackoverflow.example.document'>\n" +
"   <element xmlns:value=\"org.stackoverflow.example.value\">value:some-value</element>\n" +
"</document>";
        
        TransformerFactory xalanTransFact = new org.apache.xalan.processor.TransformerFactoryImpl();
        Templates template = xalanTransFact.newTemplates(new StreamSource(new StringReader(xslt)));
        
        StringWriter writer = new StringWriter();
        Transformer transformer = template.newTransformer();
        
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document parse = builder.parse(new InputSource(new StringReader(document)));
        
        transformer.transform(new DOMSource(parse),new StreamResult(writer));
    }
    
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...