我有一种情况,我想从一些非常大但常规的XML文件中提取一些信息(只需要使用500 Mb文件),以及XSLT将是完美的。
不幸的是,我所知道的那些XSLT实现(除了最昂贵的Saxon版本之外)不支持只读取DOM的必要部分,而是读取整个树。这导致计算机交换死亡。
有问题的XPath是
//m/e[contains(.,'foobar')
所以它基本上只是一个grep。
是否有可以执行此操作的XSLT实现?或者给出合适的“建议”的XSLT实现可以修复内存中不再需要的部分吗?
我更喜欢Java实现,但Windows和Linux都是可行的原生平台。
编辑:输入XML如下所示:
<log>
<!-- Fri Jun 26 12:09:27 CEST 2009 -->
<e h='12:09:27,284' l='org.apache.catalina.session.ManagerBase' z='1246010967284' t='ContainerBackgroundProcessor[StandardEngine[Catalina]]' v='10000'>
<m>Registering Catalina:type=Manager,path=/axsWHSweb-20090626,host=localhost</m></e>
<e h='12:09:27,284' l='org.apache.catalina.session.ManagerBase' z='1246010967284' t='ContainerBackgroundProcessor[StandardEngine[Catalina]]' v='10000'>
<m>Force random number initialization starting</m></e>
<e h='12:09:27,284' l='org.apache.catalina.session.ManagerBase' z='1246010967284' t='ContainerBackgroundProcessor[StandardEngine[Catalina]]' v='10000'>
<m>Getting message digest component for algorithm MD5</m></e>
<e h='12:09:27,284' l='org.apache.catalina.session.ManagerBase' z='1246010967284' t='ContainerBackgroundProcessor[StandardEngine[Catalina]]' v='10000'>
<m>Completed getting message digest component</m></e>
<e h='12:09:27,284' l='org.apache.catalina.session.ManagerBase' z='1246010967284' t='ContainerBackgroundProcessor[StandardEngine[Catalina]]' v='10000'>
<m>getDigest() 0</m></e>
......
</log>
Essentialy我想选择一些m节点(我知道XPath是错误的,它只是一个快速的黑客),但保持XML布局。
答案 0 :(得分:6)
考虑VTD-XML。 更多更多内存效率。您可以找到API here和基准here。
请注意,最后一个图表说DOM使用的内存至少是XML文件的5倍。毕竟这真是令人惊讶,不是吗?
作为奖励,它在解析和Xpath方面也比DOM和JDK更快:
(来源:sourceforge.net)
答案 1 :(得分:2)
您应该能够在没有全表扫描的情况下实现此功能。 '//'运算符意味着在任何级别的树中查找元素。特别是在你的尺寸的文件上运行是非常昂贵的。如果优化XPath查询或考虑设置匹配模板,则XSLT转换器可能不需要将整个文档加载到内存中。
根据您的XML示例,您希望匹配/ log / e / m [...谓词...]。这应该能够被某些XSLT处理器优化,以便不扫描//不会在哪里的完整文档。
由于您的XML文档非常简单,因此根本不使用XSLT可能更容易。 STaX是一个很棒的流API,用于处理大型XML文档。 Dom4j也很好地支持对大型文档进行查询的XPath。有关在大型文档中使用dom4j的信息,请访问:http://dom4j.sourceforge.net/dom4j-1.6.1/faq.html#large-doc
以上来源的示例:
SAXReader reader = new SAXReader();
reader.addHandler( "/ROWSET/ROW",
new ElementHandler() {
public void onStart(ElementPath path) {
// do nothing here...
}
public void onEnd(ElementPath path) {
// process a ROW element
Element row = path.getCurrent();
Element rowSet = row.getParent();
Document document = row.getDocument();
...
// prune the tree
row.detach();
}
}
);
Document document = reader.read(url);
// The document will now be complete but all the ROW elements
// will have been pruned.
// We may want to do some final processing now
...
答案 2 :(得分:1)
答案 3 :(得分:1)
我遇到了同样的问题,并且不想编写任何Java代码。我设法通过Joost解决了这个问题。
根据spec:
STX进程可能会将大型XML文档拆分为较小的片段, 将这些片段中的每一个传递给外部过滤器(例如 XSLT处理器),并将结果组合成一个大的XML结果 文档。
这正是我所需要的。我拥有的最大XML文件示例是1.5 GB,我有一个XSLT模板来处理它。使用Saxon免费版时,处理时消耗了 3GB 内存。使用Joost时,小于90MB 。
我的XML文件包含大量产品列表,每个产品都有一个复杂的XML结构。所以我不想在STX中重新实现我的XSLT,但只想分割每个产品的处理,同时为每个产品使用相同的XSLT。
以下是代码详细信息,希望对某些人有所帮助。
原始XSLT文件(这是我实现的第一个XSLT,很抱歉for-each语句使用不当):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:template match="/">
<xsl:for-each select="Products/Product">
<!-- Some XSL statements relative to "Product" element -->
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我将其转换为以下STX:
<?xml version="1.0" encoding="UTF-8"?>
<stx:transform version="1.0"
output-method="text"
output-encoding="UTF-8"
xmlns:stx="http://stx.sourceforge.net/2002/ns">
<stx:buffer name="xslt-product">
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:template match="Product">
<!-- The same XSL statements relative to "Product" element -->
</xsl:template>
</xsl:stylesheet>
</stx:buffer>
<stx:template match="/">
<stx:process-children />
</stx:template>
<stx:template match="Product">
<stx:process-self filter-method="http://www.w3.org/1999/XSL/Transform"
filter-src="buffer(xslt-product)" />
</stx:template>
</stx:transform>
当运行Joost时,我仍然需要添加Saxon库,因为我在XSLT中使用了函数,所以我需要XSLT 2.0支持。最后,运行转换的命令是这样的:
java -Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl -cp joost.jar:commons-discovery-0.5.jar:commons-logging-1.1.1.jar:saxon9he.jar net.sf.joost.Main my-source.xml my-convert.stx
最重要的是,现在我可以在低内存服务器上运行转换,而无需实现任何Java代码或重新实现原始XSLT规则!
答案 4 :(得分:0)
这是在黑暗中刺伤,也许你会笑我出门。
没有什么可以阻止您将SAX源连接到XSLT的输入;至少在理论上它很容易从SAX流中执行grep而不需要DOM。那么......想尝试一下吗?
答案 5 :(得分:0)
从xponentsoftware尝试CAX解析器。它是一个基于Microsoft的xmlreader构建的快速xml解析器。它在解析每个元素时给出完整路径,因此您可以检查路径是否为“m / e”,然后检查文本节点是否包含“foo”
答案 6 :(得分:0)
我不是Java人,我不知道我在.NET中使用的工具是否在Java世界中具有类似功能。
要在.NET中解决这个问题,我会从XmlReader
派生一个类,让它只返回我感兴趣的元素。然后我可以使用XmlReader
作为输入任何XML对象,例如XmlDocument
或XslCompiledTransform
。 XmlReader
子类基本上预处理输入流,使其看起来像一个更小,更小的XML文档,无论什么类使用它来读取。
似乎所描述的技术here是类似的。但正如我所说,我不是一个Java人。
答案 7 :(得分:0)
STX包含一个可流式的XPath子集,我相信称为STXPath;我应该记住,因为我共同编写了规范: - )
您绝对可以选择Joost并提取相关位,但请注意,STX并未获得业界的广泛认可,因此您需要对该工具的当前稳定性和支持做一些尽职调查。
答案 8 :(得分:0)
您可以通过已建议的STX / Joost来完成,但请注意,许多XSLT实现都具有SAX流模式,并且不需要将所有内容保留在内存中。您只需要确保您的XSLT文件没有查找任何错误的轴。
但是如果我是你并且真的想要表现我会在STaX中做到。它简单,标准,快速。它在Java 6中开箱即用,尽管您也可以使用Woodstox实现更好的实现。
对于您列出的xpath,实现很简单。缺点是你需要维护更多的代码,而且它不像XPath那样富有表现力和高水平,就像你在Joost或XSLT中那样。
答案 9 :(得分:0)
编写xslt以返回首选xml布局中的值,该布局仅包含来自largeXmls所需的值。
但是,如果要进一步使用Java处理值,则:
使用StreamSource通过xslt解析xml的示例:
使用的包裹:
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
代码:
String xmlStr = "<A><b>value</b><c>value</c></A>";
File xslt = new ClassPathResource("xslt/Transformer.xslt").getFile();
Source xsltSource = new StreamSource(xslt);
Source xmlSource = new StreamSource(new StringReader(xmlStr));
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer(xsltSource);
StringWriter stringWriter = new StringWriter();
transformer.transform(xmlSource, new StreamResult(stringWriter));
String response = stringWriter.toString();