Java中的XSLT转换极其缓慢

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

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

这是一个示例代码(没什么特别的):

TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer transformer = null; try { transformer = tFactory.newTransformer( new StreamSource("/home/pd/XSLT/transf.xslt")); } catch (TransformerConfigurationException e) { e.printStackTrace(); } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.out.println("START"); try { transformer.transform(new SAXSource(new InputSource( new FileInputStream("/home/pd/XSLT/wordpress.xml"))), new StreamResult(outputStream)); } catch (TransformerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println("STOP"); System.out.println(new String(outputStream.toByteArray())); 

它位于START和STOP之间,java“暂停”2分钟。 如果我看看处理器或内存使用情况,没有任何增加。 它看起来真的是JVM停止了……

您是否有任何转换长度超过50的XML(这是随机数;))的经验? 当我读到XSLT时,总是需要构建DOM树才能完成它的工作。 快速转型对我来说至关重要。

提前谢谢,Piotr

示例HTML文件是否使用命名空间? 如果是这样,您的XML解析器可能正在尝试从命名空间URI中检索内容(可能是模式)。 如果每次运行只需要两分钟就可能发生这种情况 – 可能是一次或多次TCP超时。

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

要解决此问题,您可以实现自己的EntityResolver ,它基本上禁用基于URL的解析。 您可能需要使用DOM – 请参阅DocumentBuildersetEntityResolver方法。

这是使用DOM和禁用分辨率的示例(注意 – 这是未经测试的):

 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(); } 

如果要使用SAX,则必须使用带有XMLReaderSAXSource ,该XMLReader使用您的自定义解析器。

那些发布可能与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; } } }); 

机会是问题,不是调用transfomer.transform。 你更有可能在你的xslt中做一些永远需要的东西。 我的建议是使用像Oxygen或XML Spy这样的工具来分析你的XSLT并找出执行时间最长的模板。 一旦确定了这一点,就可以开始优化模板了。

如果您正在Android设备上调试代码,请确保在没有附加到该进程的eclipse的情况下进行尝试。 当我调试我的应用程序xslt转换需要8秒,其中相同的过程在本机代码中的ios上花了十分之一秒。 一旦我运行没有附加eclipse的代码,该过程花费了相当多的时间给基于c的对应物。