我已经实现了一个简单的XPath扩展函数,该函数将在XSLT转换期间由Xalan-J调用。调用部分很简单,但我不理解的是,函数的实现如何访问被转换文档的名称空间上下文(而不是转换上下文,作为扩展函数的第一个参数提供)。我需要这个上下文来解析实际为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函数时显示名称空间信息?
发布于 2020-11-09 17:33:12
根据@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));
}
}https://stackoverflow.com/questions/64748688
复制相似问题