我真的阅读并测试了很多,但我没有得到一个有效的java解决方案:
我有一个大的xml文件(超过100MB),现在通过JAXB处理。目的是每次使用一个root子将xml分成许多xmls 重要提示:由于文件大小,首选sax-way。
我发现了很多关于xsl:result-document的信息,但我发现没办法让它从java运行,我不太确定,是否可以保持所需的内存不足。
这是我的Java代码:
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
public class TestParse {
public static void main(final String[] args) throws Throwable {
final TransformerFactory factory = TransformerFactory.newInstance();
final Transformer transformer = factory.newTransformer(new StreamSource("D:\\split.xsl"));
final StreamSource in = new StreamSource("D:\\input.xml");
final StreamResult out = new StreamResult("D:\\output.xml");
transformer.transform(in, out);
}
这是一个示例-xml(" input.xml"):
<?xml version="1.0" encoding="ISO-8859-1"?>
<Taskname>
<Item attr="ab" attr2="c">
<MoreNodes>...</MoreNodes>
</Item>
<Item attr="xy" attr2="z">
<MoreNodes>...</MoreNodes>
</Item>
<!-- ...and many items more -->
</Taskname>
这是我的xsl(split.xsl):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:strip-space elements="*"/>
<xsl:param name="dir" select="'file:///D://'"/>
<xsl:template match="Item">
<xsl:result-document href="{$dir}section{position()}.xml" method="xml">
<Taskname>
<xsl:copy-of select="." />
</Taskname>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
因此,一个结果-xml应该如下所示:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Taskname>
<Item attr="..." attr2="...">
<MoreNodes>...</MoreNodes>
</Item>
</Taskname>
我的问题:
我现在真的不知道如何获得xslt的不同输出以及更多,我需要它们作为Streams而不是文件 - 我会逐项需要它们(比如sax&# 39; endElement)使用更少的内存。
也许,还有一种比使用xslt更好的方法,请告诉我。
答案 0 :(得分:1)
首先,如果你想避免在内存中为源文档构建一个树,那么你将不得不使用XSLT 3.0流程运行它 - 这意味着你需要一个Saxon-EE许可证。 (但是,以传统方式处理100Mb文件非常可行,内存中有一棵树。)
其次,如果您希望将xsl:result-document的输出捕获为内存流而不是写入filestore,那么在Saxon中实现此目的的方法是编写并注册一个OutputURIResolver。这将针对每个结果文档调用一次,并且可以指定接收文档的目标(例如StreamResult或SAXResult)。
答案 1 :(得分:0)
我可能不需要XSLT来完成这项任务,只需直接使用类似StAX API的东西。但这取决于你最终想要对拆分文件做什么。你在问题中提到了JAXB,请注意,JAXB Unmarshaller可以从StAX XMLStreamReader
读取,这允许您使用一种“半流”处理模型,您可以通过输入文件来解组输入文件它一次Item
个。假设您有一个代表Item
元素类型的Item
类:
JAXBContext ctx = JAXBContext.newInstance(Item.class);
Unmarshaller u = ctx.createUnmarshaller();
XMLInputFactory inFactory = XMLInputFactory.newFactory();
try(InputStream stream = Files.newInputStream(Paths.get("input.xml"))) {
XMLStreamReader reader = inFactory.createXMLStreamReader(stream);
try {
reader.nextTag(); // the root Taskname start tag
reader.nextTag(); // the start tag of the first Item, if there is
// one, the end of the Taskname if there isn't
while(reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
JAXBElement<Item> theItem = u.unmarshal(reader, Item.class);
// do whatever you want to do with this item
process(theItem.getValue());
// this is an oddity of the JAXB API - when unmarshalling from
// a stream reader the reader is left pointing to the event
// *after* the closing tag, not to the closing tag itself,
// so whether or not we need to advance to the next tag depends
// whether there is whitespace between the close of one Item
// and the start of the next.
if(reader.getEventType() != XMLStreamConstants.START_ELEMENT &&
reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
reader.nextTag();
}
}
} finally {
reader.close();
}
}