Java中极其缓慢的XSLT转换

Java中极其缓慢的XSLT转换,java,xml,xslt,Java,Xml,Xslt,我尝试使用XSLT转换XML文档。作为输入,我有www.wordpress.org XHTML源代码,XSLT是检索站点标题的虚拟示例(实际上它什么也做不了——它不会改变任何东西) 我使用的每一个API或库,转换大约需要2分钟!如果你看看wordpress.org的源代码,你会发现它只有183行代码。正如我在谷歌上搜索到的,这可能是由于DOM树的构建。不管XSLT多么简单,它总是需要2分钟——所以它证实了它和DOM构建相关的想法,但无论如何,我认为它不应该需要2分钟 下面是一个示例代码(没有什么

我尝试使用XSLT转换XML文档。作为输入,我有www.wordpress.org XHTML源代码,XSLT是检索站点标题的虚拟示例(实际上它什么也做不了——它不会改变任何东西)

我使用的每一个API或库,转换大约需要2分钟!如果你看看wordpress.org的源代码,你会发现它只有183行代码。正如我在谷歌上搜索到的,这可能是由于DOM树的构建。不管XSLT多么简单,它总是需要2分钟——所以它证实了它和DOM构建相关的想法,但无论如何,我认为它不应该需要2分钟

下面是一个示例代码(没有什么特别之处):

在启动和停止之间,java会“暂停”2分钟。如果我看一下处理器或内存的使用情况,没有任何增加。看起来真的停止了

您是否有转换超过50行(这是随机数;)的XML的经验?正如我所读到的,XSLT总是需要构建DOM树来完成它的工作。快速转变对我来说至关重要

提前感谢,,
Piotr

问题可能不在call Transformer.transform上。更有可能的情况是,您正在xslt中做一些花费了很长时间的事情。我的建议是使用Oxygen或XMLSpy之类的工具来分析XSLT,并找出哪些模板执行时间最长。一旦确定了这一点,就可以开始优化模板

示例HTML文件是否使用名称空间?如果是这样,XML解析器可能正试图从名称空间URI检索内容(可能是模式)。如果每次运行只需两分钟,则可能出现这种情况——可能是一次或多次TCP超时

您可以通过计时实例化
InputSource
对象(实际解析WordPress XML的地方)所需的时间来验证这一点,因为这可能是导致延迟的原因。查看您发布的示例文件后,它确实包含一个声明的命名空间(
xmlns=)http://www.w3.org/1999/xhtml“

为了解决这个问题,您可以实现自己的解决方案,这实际上禁用了基于URL的解析。您可能需要使用DOM——请参见
DocumentBuilder
的方法

下面是一个使用DOM和禁用解析的示例(注意——这是未经测试的):


如果您想使用SAX,则必须将a与使用自定义解析器的一起使用。

发布答案可能存在于EntityResolver的评论者可能是正确的。但是,解决方案可能不是简单地不加载模式,而是从本地文件系统加载模式

所以你可以这样做

  db.setEntityResolver(new EntityResolver() {

    @Override
    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
        try {
        FileInputStream fis = new FileInputStream(new File("classpath:xsd/" + systemId));
        InputSource is  = new InputSource(fis);
        return is
    } catch (FileNotFoundException ex) {
        logger.error("File Not found", ex);
        return null;
    }
    }
});

如果您正在android设备上调试代码,请确保在没有eclipse连接到进程的情况下进行尝试。当我调试我的应用程序时,xslt转换需要8秒,而同样的过程在ios上本机代码只需要十分之一秒。一旦我在没有eclipse连接的情况下运行代码,这个过程花费的时间与基于c的代码相当。

wordpress.xml有多大?是www.wordpress.org XHTML-它有183行长(已经包装好)您可以发布xml和xslt文件吗?另外:如果您的输出是ByteArrayOutputStream,这可能会给您带来问题,因为通常您的输入是RTF-8,而不是您的输出。这是输入XML(www.wordpress.org网站源代码),问题出在xslt中。。发布这篇文章,也许有人可以帮助您。事实上,一些XML解析器会这样做来验证文档。您可以通过配置将其关闭。但是,这是特定于XML解析器的。OP必须提到他正在使用的JAXP实现(例如Xerces、Saxon、Crimson等),然后可以在文档中查阅它。谢谢!你说得对,这都是关于下载DTD和包含在这个DTD中的所有其他文件。使用EntityResolver的两种解决方案都很好,但有一个限制-我需要知道需要哪个DTD,准备它的缓存实例并使其在EntityResolver中可用。如果我不知道输入XML(它是客户端在运行时给出的),该怎么办?如果是这样,我不知道需要哪种DTD。有没有办法“劫持”transformer下载的DTD(假设我的实体解析器返回null),缓存它,以及下次需要此DTD时从缓存中返回它?在您开始在自定义解析器中缓存/预加载DTD之前,您应该确定您试图解决的问题是否需要DTD。因为您试图将XSLT应用于XHTML,所以我猜不会(注意DTD是在XML解析阶段解析的,而不是转换阶段)。比return null更好的(至少对于xerces/xalan而言)可能是return new InputSource(new StringReader(“”))。和/或factory.setFeature(“,false);感谢JasonPlutext返回空值;仍然需要很长时间,所以我返回了新的InputSource(new StringReader(“”);而且现在速度很快。绝对-问题在于XSLT代码。海报选择向我们展示Java代码而不是XSLT代码这一事实表明,他们真的不知道从哪里开始研究这个问题。谢谢!您是对的,这一切都是关于下载DTD和此DTD中包含的所有其他文件。使用EntityResolver的两种解决方案都很好,但有一个限制-我需要知道需要哪个DTD,准备它的缓存实例并使其在EntityResolver中可用。如果我不知道输入XML(它是客户端在运行时给出的),该怎么办?如果是这样,我不知道需要哪种DTD。有没有办法“劫持”transformer下载的DTD(假设我的实体解析器返回null),缓存它,以及下次需要此DTD时从缓存返回它?这应该是相对简单的。首先检查是否存在具有DTD systemId的文件(systemId是文件名本身)。如果该文件在本地不存在,则使用publicId作为URL,获取
try {
    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbFactory.newDocumentBuilder();
    db.setEntityResolver(new EntityResolver() {

        @Override
        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
            return null; // Never resolve any IDs
        }
    });

    System.out.println("BUILDING DOM");

    Document doc = db.parse(new FileInputStream("/home/pd/XSLT/wordpress.xml"));

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    TransformerFactory tFactory = TransformerFactory.newInstance();
    Transformer transformer = tFactory.newTransformer(
        new StreamSource("/home/pd/XSLT/transf.xslt"));

    System.out.println("RUNNING TRANSFORM");

    transformer.transform(
            new DOMSource(doc.getDocumentElement()),
            new StreamResult(outputStream));

    System.out.println("TRANSFORMED CONTENTS BELOW");
    System.out.println(outputStream.toString());
} catch (Exception e) {
    e.printStackTrace();
}
  db.setEntityResolver(new EntityResolver() {

    @Override
    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
        try {
        FileInputStream fis = new FileInputStream(new File("classpath:xsd/" + systemId));
        InputSource is  = new InputSource(fis);
        return is
    } catch (FileNotFoundException ex) {
        logger.error("File Not found", ex);
        return null;
    }
    }
});