Java 为什么我的转换器输出它的样式表,而不是它所代表的转换?

Java 为什么我的转换器输出它的样式表,而不是它所代表的转换?,java,xslt,Java,Xslt,简单地说,我的问题是:当我从一个有效的样式表构建一个转换器,并调用它的transform(input,output)方法时,我似乎得到了样式表本身存储在output中的内容,而不是input的转换 (在底部更新。这个问题现在是。) 我正在使用Java8对basicjavax.xml.transformAPI进行最基本的基本调用 这里是所有的细节 我有一个基本的XML文档,看起来是这样的(我们将看到,它几乎不重要): print()方法非常简单: private static final void

简单地说,我的问题是:当我从一个有效的样式表构建一个
转换器
,并调用它的
transform(input,output)
方法时,我似乎得到了样式表本身存储在
output
中的内容,而不是
input
的转换

在底部更新。这个问题现在是。

我正在使用Java8对basic
javax.xml.transform
API进行最基本的基本调用

这里是所有的细节

我有一个基本的XML文档,看起来是这样的(我们将看到,它几乎不重要):

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):

*****
*****
*****
*****
在这个最基本的教程级示例中,我做错了什么

更新#1:在我的示例中,我使用
DOMSource
为我的样式表构建了一个
源代码。如果我切换到使用
StreamSource
来“存放”我的样式表,我的输出是正确的。为什么会这样?使用
DOMSource
包装解析XSLT样式表的
文档
是否存在固有的问题

更新#2:感谢上帝;有一个


更新#3:
TransformerFactory
允许您在其方法中使用任何
源代码
实现。但是如果
Source
实现恰好是
DOMSource
,那么您最好希望调用者使用
DocumentBuilderFactory
生成它,否则文档转换的结果将是样式表本身。这非常奇怪,而且在这些API的设计中有一点缺陷。

事实证明,尽管
TransformerFactory
允许您为您的应用程序使用一个实现,但如果您碰巧(当然是在不知不觉中)使用了由调用方构造的,那么您将得到样式表的输出作为您的转换(!)


这个故事的寓意是:告诉你的调用者该做什么(?!),或者如果你对转换过程本身有任何控制权,确保你永远不会接受一个
源代码
,它实际上是一个
DOMSource

使用
StreamSource
(而不是
DOMSource
)会产生不同的结果吗?(我的意思是尝试使用:
final Transformer t=tf.newTemplates(new-DOMSource(foobarXsltStream)).newTransformer()
t.transform(new-DOMSource(foobarStream),result);
)。方法不同,结果可能会给你一个更好的问题线索(这样做也更有效)。是的,这正是问题所在。如果(间接地)在
DOMSource
中构建
文档的
DocumentBuilderFactory
不知道名称空间,那么您将获得样式表作为输出。即使
Transformer
中的合同接受任何类型的
源代码
。为什么会出现这种情况对我来说仍然是一个谜。虽然答案在经验上可能是正确的(可能是针对JDK附带的Xalan版本),但我不明白为什么会发生这种情况。我希望有一条消息说样式表无效。但这个故事的另一个寓意是,避免像瘟疫一样的死亡。总有更好的选择。我也不知道!当我有时间的时候,我将深入研究Xalan的实现,并跟踪这个问题。我讨厌觉得自己笨手笨脚。
<?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>
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("*****");

}
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));
}
*****
<?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>
*****