如何将XML文档拆分为三份(甚至更好的是n个)?
我想使用一种我熟悉的Java,C#,Ruby,PHP,C / C ++语言,尽管使用任何语言或者伪代码的示例都非常受欢迎。
将大型XML文档拆分为仍然有效的较小部分的最佳方法是什么?出于我的目的,我需要将它们拆分为大约三分之四或者四分之一,但是为了提供示例,将它们拆分为n个组件将是很好的。
解决方案
回答
当然,我们总是可以提取顶级元素(这是否取决于粒度)。在C#中,我们将使用XmlDocument类。例如,如果XML文件如下所示:
<Document> <Piece> Some text </Piece> <Piece> Some other text </Piece> </Document>
那么我们将使用如下代码提取所有片段:
XmlDocument doc = new XmlDocument(); doc.Load("<path to xml file>"); XmlNodeList nl = doc.GetElementsByTagName("Piece"); foreach (XmlNode n in nl) { // Do something with each Piece node }
一旦有了节点,就可以在代码中对它们进行处理,或者可以将节点的整个文本传输到其自己的XML文档中,然后像对待独立的XML片段一样对其进行操作(包括保存它)。回到磁盘等)。
回答
这更多是评论,而不是答案,但不会:
XmlDocument doc = new XmlDocument(); doc.Load("path");
一次读取整个文件?只是认为我应该提出一点,因为从托马斯的问题来看,他担心读取大文件,并希望将其分解。
回答
正如DannySmurf谈到的那样,这全都与xml文档的结构有关。
如果我们只有两个巨大的"顶层"标签,将很难以一种方式将其拆分,从而可以将其重新合并在一起并作为有效的XML逐段读取。
给定一个文档,其中包含许多单独的部分(如DannySmurfs示例中的部分),这应该很容易。
伪C中的一些粗略代码:
int nrOfPieces = 5; XmlDocument xmlOriginal = some input parameter.. // construct the list we need, and fill it with XmlDocuments.. var xmlList = new List<XmlDocument>(); for (int i = 0; i < nrOfPieces ; i++) { var xmlDoc = new XmlDocument(); xmlDoc.ChildNodes.Add(new XmlNode(xmlOriginal.FistNode.Name)); xmlList.Add(xmlDoc); } var nodeList = xmlOriginal.GetElementsByTagName("Piece")M // Copy the nodes from the original into the pieces.. for (int i = 0; i < nodeList .Count; i++) { var xmlDoc = xmlList[i % nrOfPieces]; var nodeToCopy = nodeList[i].Clone(); xmlDoc.FirstNode.ChildNodes.Add(nodeToCopy); }
这应该为我们提供n份具有正确xml的文档,并且可以将它们重新合并在一起。
但同样,它取决于xml文件。
回答
它将立即读取整个文件。但是,以我的经验,如果我们只是读取文件,进行一些处理(即分解)然后继续进行工作,则XmlDocument将会经历创建/读取/收集周期的如此之快,以至于可能无关紧要。
当然,这取决于什么是"大"文件。如果它是一个30 MB的XML文件(对于XML文件,我认为它会很大),那么它可能不会有任何区别。如果它是一个500 MB的XML文件,则在没有大量RAM的系统上使用XmlDocument会变得非常麻烦(但是,在那种情况下,我认为用XmlReader手动读取文件的时间会更重要。障碍)。
回答
我们似乎正在使用Cand .NET 3.5. 我遇到过一些建议使用XmlReader在文件流上使用收益类型算法的帖子。
以下是几篇博客文章,可入门:
- 使用Linq流到SQL第1部分
- 使用Linq To SQL进行流传输第2部分
回答
不知道我们正在执行哪种类型的处理,但是对于非常大的XML,我一直是基于事件的处理的拥护者。也许这是我的Java背景,但是我确实很喜欢SAX。我们需要进行自己的状态管理,但是一旦超过了状态管理,它就是解析XML的一种非常有效的方法。
http://saxdotnet.sourceforge.net/
回答
我要和你一起在这上面加油。对于非常大的文件,SAX(或者任何其他流解析器)将在处理中提供很大的帮助。使用DOM,我们可以只收集顶级节点,但是我们仍然必须解析整个文档才能做到这一点……使用流解析器和基于事件的处理,可以"跳过"我们不感兴趣的节点。使处理速度更快。
回答
如果我们对Perl不完全敏感,那么XML :: Twig附带了一个名为xml_split的工具,该工具可以拆分文档,从而生成格式正确的XML部分。我们可以按大小或者XPath表达式在树的级别上进行拆分。
回答
使用DOM解析XML文档无法扩展。
该Groovy脚本使用StAX(XML的Streaming API)在顶级元素(与根文档的第一个子元素共享相同的QName)之间分割XML文档。它非常快,可以处理任意大文件,并且在要将大批处理文件拆分成较小的部分时非常有用。
需要Java 6或者StAX API上的Groovy以及CLASSPATH中的Woodstox之类的实现
import javax.xml.stream.* pieces = 5 input = "input.xml" output = "output_%04d.xml" eventFactory = XMLEventFactory.newInstance() fileNumber = elementCount = 0 def createEventReader() { reader = XMLInputFactory.newInstance().createXMLEventReader(new FileInputStream(input)) start = reader.next() root = reader.nextTag() firstChild = reader.nextTag() return reader } def createNextEventWriter () { println "Writing to '${filename = String.format(output, ++fileNumber)}'" writer = XMLOutputFactory.newInstance().createXMLEventWriter(new FileOutputStream(filename), start.characterEncodingScheme) writer.add(start) writer.add(root) return writer } elements = createEventReader().findAll { it.startElement && it.name == firstChild.name }.size() println "Splitting ${elements} <${firstChild.name.localPart}> elements into ${pieces} pieces" chunkSize = elements / pieces writer = createNextEventWriter() writer.add(firstChild) createEventReader().each { if (it.startElement && it.name == firstChild.name) { if (++elementCount > chunkSize) { writer.add(eventFactory.createEndDocument()) writer.flush() writer = createNextEventWriter() elementCount = 0 } } writer.add(it) } writer.flush()
回答
我制作了一个YouTube视频,展示了如何使用foxe(Firstobject提供的免费XML编辑器)仅使用少量内存来分割XML文件,而不管输入和输出文件的大小如何。
此CMarkup XML读取器(拉解析器)和XML写入器解决方案的内存使用情况取决于子文档的大小(分别从输入文件传输到输出文件),或者最小块大小为16 KB。
split() { CMarkup xmlInput, xmlOutput; xmlInput.Open( "50MB.xml", MDF_READFILE ); int nObjectCount = 0, nFileCount = 0; while ( xmlInput.FindElem("//ACT") ) { if ( nObjectCount == 0 ) { ++nFileCount; xmlOutput.Open( "piece" + nFileCount + ".xml", MDF_WRITEFILE ); xmlOutput.AddElem( "root" ); xmlOutput.IntoElem(); } xmlOutput.AddSubDoc( xmlInput.GetSubDoc() ); ++nObjectCount; if ( nObjectCount == 5 ) { xmlOutput.Close(); nObjectCount = 0; } } if ( nObjectCount ) xmlOutput.Close(); xmlInput.Close(); return nFileCount; }