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

时间:2015-03-25 00:19:27

标签: java xslt

我的问题简要说明:当我从有效的样式表构建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的设计中存在缺陷。

1 个答案:

答案 0 :(得分:1)

事实证明,尽管TransformerFactory允许您为Source使用stylesheet实施,但如果您(当然在不知不觉中)发生了使用DOMSourcedid not declare his associated DocumentBuilderFactory as namespace-aware由{{3}}的调用者构建,然后您将样式表的输出作为转换(!)。

故事的寓意是:告诉你的来电者该怎么做(?!)或 - 如果你对转型过程本身有任何控制权 - 确保你不接受Source那个实际上是DOMSource