使用SAXTransformerFactory和TransformerHandlers访问XSLT中的未解析实体

时间:2017-04-13 08:23:44

标签: java xslt saxon

使用XPath函数unparsed-entity-uri()检索未解析的实体URI时遇到了一些问题。

我在"Efficient XSLT pipeline in Java" question中使用SAXTransformerFactory,因为我需要执行转换链(即应用多个XSLT转换,并使用转换结果作为第二个输入变换)。

我发现我无法检索未解析的实体,感谢下面的代码。 实际上它适用于Xalan,但不适用于Saxon-HE(版本9.7.0) - 但我需要撒克逊,因为我更喜欢XSLT 2.0(即使在下面的代码中&#39 ;没有什么特别针对XSLT 2,它只是为了提供一个例子)。如果我不使用TransformerHandler,它也适用于Saxon,例如: stf.newTransformer(new StreamSource("transfo.xsl")).transform(new StreamSource("input.xsl"), new StreamResult(System.out))将产生所需的输出。

我忘记了配置步骤吗?

    // use "org.apache.xalan.processor.TransformerFactoryImpl" for Xalan
    String transformerFactoryClassName = "net.sf.saxon.TransformerFactoryImpl";
    SAXTransformerFactory stf = (SAXTransformerFactory) TransformerFactory.newInstance(transformerFactoryClassName,
            LaunchSimpleTransformationUnparsedEntities.class.getClassLoader());
    try {
        TransformerHandler thTransf = stf
                .newTransformerHandler(new StreamSource("transfo.xsl"));

        // output the result in console
        thTransf.setResult(new StreamResult(System.out));

        // Launch transformation of input.xml
        Transformer t = stf.newTransformer();
        t.transform(new StreamSource("input.xml"),
                new SAXResult(thTransf));

    } catch (TransformerConfigurationException e) {
        e.printStackTrace();
    } catch (TransformerException e) {
        e.printStackTrace();
    }

在输入中,我有(对于input.xml):

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE book
[<!ENTITY cover_hadrien SYSTEM "images/covers/cover_hadrien.jpg" NDATA jpeg>]>
<book>
  <title>Les mémoires d'Hadrien</title>
  <author>Marguerite Yourcenar</author>
  <cover imgref="cover_hadrien" />
</book>

和示例XSLT(对于transfo.xsl):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:template match="cover">
      <xsl:copy>
        <xsl:value-of select="unparsed-entity-uri(@imgref)"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

因此,我希望有类似的东西:

<?xml version="1.0" encoding="UTF-8"?><book>
  <title>Les mémoires d'Hadrien</title>
  <author>Marguerite Yourcenar</author>
  <cover>images/covers/cover_hadrien.jpg</cover>
</book>

但使用Saxon执行转换时<cover>为空。

2 个答案:

答案 0 :(得分:1)

有趣的观察。实际上问题不在于Saxon的TransformerHandler,而在于使用SAXTransformerFactory.newTransformer()获得的“身份变换器”:身份变换器不会将未解析的实体传递给线路。这主要是因为Saxon的身份转换器正在重用XSLT引擎的一部分,而XSLT没有提供任何转换方式来输出结果中未解析的实体。如果您将SAX解析器输出直接发送到TransformerHandler,而不是通过身份转换器,那么我认为它都可以工作。

与JAXP相关的所有事情一样,SAXTransformerFactory.newTransformer()的规范令人愤慨地含糊不清。它说的是返回的Transformer 执行Source到Result的副本。即“身份变换”。究竟什么是副本?我认为Saxon的解释是它相当于进行XSLT身份转换的效果 - 它会丢失未解析的实体(以及其他东西,如CDATA部分,DTD等)。

顺便提一下,XSLT 2.0指定unparsed-entity-uri()的结果应该是绝对URI(XSLT 1.0没有说明任何内容),所以即使修复了这个问题,Saxon输出也会不同。 / p>

在这里作为Saxon问题输入:https://saxonica.plan.io/issues/3201如果我们不传递SAX DTDHandler所期望的所有其他事件,我认为我们需要小心将未解析的实体传递给SAXResult - 我们'肯定不会改变Saxon身份变换器以保留未在XDM中建模的东西(如DTD声明)。

答案 1 :(得分:0)

确实,按照@ MichaelKay的详细信息,以正确的方式启动转换:

        // launch transformation of input.xml
        XMLReader reader = XMLReaderFactory.createXMLReader();
        reader.setContentHandler(thTransf);
        reader.setDTDHandler(thTransf);
        reader.parse(new InputSource(input.xml"));

(这将取代以下行:

        // Launch transformation of input.xml
        Transformer t = stf.newTransformer();
        t.transform(new StreamSource("input.xml"),
                new SAXResult(thTransf));
最初使用的