我的问题简要说明:当我从有效的样式表构建Transformer
并调用其transform(input, output)
方法时,我似乎将样式表本身的内容存储在output
中,而不是input
的转变。
(底部更新。此问题现在为answered。)
我正在使用Java 8进行基本javax.xml.transform
API的所有可能基本调用中最基本的调用。
以下是所有细节。
我有一个基本的XML文档,看起来像这样(因为我们会看到,这几乎不重要):
<?xml version="1.0" encoding="UTF-8"?>
<doc>
<target-store name="foobar">
<target-key name="abc"/>
<target-key name="def"/>
</target-store>
<testing>
<target-store>
<target-key name="ghi">
<bogus/>
</target-key>
</target-store>
</testing>
</doc>
我有一个看起来像这样的XSLT文件,尽管我们很快就会看到它并不重要:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- First the identity transformation. -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="target-store">
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:with-param name="u" select="'bill'"/>
<xsl:with-param name="p" select="'gates'"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="target-key">
<xsl:param name="u" select="'scott'"/>
<xsl:param name="p" select="'tiger'"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<username><xsl:value-of select="$u"/></username>
<password><xsl:value-of select="$p"/></password>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
(将这些原始值插入几个不同的online XSLT testers会产生我想要的转换输出。将伪造的字符放入XSLT文件会产生错误,因此它会被成功读取和解析,因此它是有效的样式表文件。)
如果我运行以下(基本)Java转换代码,则输出样式表,而不是它所代表的转换。 Whiskey Tango Foxtrot。
我不能为我的生活理解为什么会这样。这是代码,然后我将粘贴输出:
public void testRawAPIs() throws Exception {
final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
assertNotNull(ccl);
final URL foobarXml = ccl.getResource("foobar.xml");
assertNotNull(foobarXml);
final URL foobarXslt = ccl.getResource("foobar.xslt");
assertNotNull(foobarXslt);
try (final InputStream foobarStream = new BufferedInputStream(foobarXml.openStream());
final InputStream foobarXsltStream = new BufferedInputStream(foobarXslt.openStream())) {
System.out.println("*****");
// db is set up elsewhere as DocumentFactoryBuilder.newInstance().newDocumentBuilder().
final Document foobarDocument = db.parse(foobarStream);
assertNotNull(foobarDocument);
print(foobarDocument);
System.out.println("*****");
final Document foobarXsltDocument = db.parse(foobarXsltStream);
assertNotNull(foobarXsltDocument);
print(foobarXsltDocument);
System.out.println("*****");
// tf is set up by JUnit elsewhere as TransformerFactory.newInstance().
final Transformer t = tf.newTemplates(new DOMSource(foobarXsltDocument)).newTransformer();
assertNotNull(t);
final DOMResult result = new DOMResult();
t.transform(new DOMSource(foobarDocument), result);
// TODO FIXME: for some reason, this prints out the STYLESHEET. WTF.
print((Document)result.getNode());
System.out.println("*****");
}
print()
方法很简单:
private static final void print(final Document document) throws Exception {
print(document, new BufferedWriter(new OutputStreamWriter(System.out, "UTF-8")), 2);
}
private static final void print(final Document document, final Writer writer, final int indent) throws Exception {
final Transformer transformer = tf.newTransformer();
assertNotNull(transformer);
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent));
transformer.transform(new DOMSource(document), new StreamResult(writer));
}
最后,输出。如您所见,原始文档首先打印,正如我所期望的那样,然后是明确打印的样式表,正如我所期望的那样,接着是...样式表的标准化版本(WTF):
*****
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<doc>
<target-store name="foobar">
<target-key name="abc"/>
<target-key name="def"/>
</target-store>
<testing>
<target-store>
<target-key name="ghi">
<bogus/>
</target-key>
</target-store>
</testing>
</doc>
*****
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- First the identity transformation. -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="target-store">
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:with-param name="u" select="'bill'"/>
<xsl:with-param name="p" select="'gates'"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="target-key">
<xsl:param name="u" select="'scott'"/>
<xsl:param name="p" select="'tiger'"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<username>
<xsl:value-of select="$u"/>
</username>
<password>
<xsl:value-of select="$p"/>
</password>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
*****
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="target-store">
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:with-param name="u" select="'bill'"/>
<xsl:with-param name="p" select="'gates'"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="target-key">
<xsl:param name="u" select="'scott'"/>
<xsl:param name="p" select="'tiger'"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<username>
<xsl:value-of select="$u"/>
</username>
<password>
<xsl:value-of select="$p"/>
</password>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
*****
在这个最基本的教程级示例中,我做错了什么?
更新#1:在我的示例中,我使用Source
为我的样式表构建DOMSource
。如果我切换到使用StreamSource
到&#34;房子&#34;我的样式表,我的输出是正确的。为什么是这样?使用DOMSource
来包装Document
是解析XSLT样式表的结果有什么本质上的错误吗?
更新#2:感谢主;那是related question。
更新#3: TransformerFactory
允许您在其newTransformer(Source)
方法中使用任何Source
实施。但如果Source
实施恰好是DOMSource
,那么您最好希望调用者使用namespace aware DocumentBuilderFactory
生成它,否则文档转换的结果将是样式表本身。这非常奇怪,并且在这些API的设计中存在缺陷。
答案 0 :(得分:1)
事实证明,尽管TransformerFactory
允许您为Source
使用stylesheet实施,但如果您(当然在不知不觉中)发生了使用DOMSource
的did not declare his associated DocumentBuilderFactory
as namespace-aware由{{3}}的调用者构建,然后您将样式表的输出作为转换(!)。
故事的寓意是:告诉你的来电者该怎么做(?!)或 - 如果你对转型过程本身有任何控制权 - 确保你不接受Source
那个实际上是DOMSource
。