我实现了一个简单的XPath扩展函数,该函数旨在由Xalan-J在XSLT转换期间调用。调用部分很琐碎,但我不明白的是,函数的实现应该如何访问正在转换的文档的命名空间上下文(而不是作为扩展函数的第一个参数提供的转换上下文(。我需要这个上下文来解析实际上是QNames的元素文本值的名称空间。
下面是一个需要转换的示例文档:
<document xmlns='org.stackoverflow.example.document'>
<element xmlns:value="org.stackoverflow.example.value">value:some-value</element>
</document>
value:some-value
是我想要解析的QName文本值的一个示例,它表示org.stackoverflow.example.value
命名空间中的some-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));
}
}