我想使用我熟悉的语言 - Java、C#、Ruby、PHP、C/C++,尽管任何语言或伪代码的示例都非常受欢迎。
将大型 XML 文档拆分为仍然是有效 XML 的较小部分的最佳方法是什么?出于我的目的,我需要将它们分成大约三分之一或四分之一,但为了提供示例,将它们分成 n 个组件会很好。
我想使用我熟悉的语言 - Java、C#、Ruby、PHP、C/C++,尽管任何语言或伪代码的示例都非常受欢迎。
将大型 XML 文档拆分为仍然是有效 XML 的较小部分的最佳方法是什么?出于我的目的,我需要将它们分成大约三分之一或四分之一,但为了提供示例,将它们分成 n 个组件会很好。
使用 DOM 解析 XML 文档无法扩展。
这个Groovy脚本使用 StAX(XML 流 API)在顶级元素(与根文档的第一个子元素共享相同的 QName)之间拆分 XML 文档。它非常快,可以处理任意大型文档,并且在您要将大型批处理文件拆分为较小的部分时非常有用。
需要 Java 6 上的 Groovy 或CLASSPATH 中的 StAX API 和实现,例如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()
当然,您总是可以提取顶级元素(这是否是您想要的粒度取决于您)。在 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 片段一样(包括保存它)回磁盘等)。
正如 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);
}
这应该为您提供具有正确 xml 的 n 文档以及将它们重新合并在一起的可能性。
但同样,它取决于 xml 文件。
这更像是评论而不是答案,但不会:
XmlDocument doc = new XmlDocument();
doc.Load("path");
一次读取整个文件?只是想我应该提出这一点,因为从托马斯的问题来看,他担心读取大文件并想打破这个过程..
它会一次读取整个文件。但是,根据我的经验,如果您只是在读取文件、进行一些处理(即分解它)然后继续您的工作,那么 XmlDocument 将很快完成它的创建/读取/收集周期,以至于可能没关系。
当然,这取决于“大”文件是什么。如果它是一个 30 MB 的 XML 文件(我认为它对于 XML 文件来说很大),它可能不会有任何区别。如果它是一个 500 MB 的 XML 文件,那么在没有大量 RAM 的系统上使用 XmlDocument 将变得非常有问题(但是,在这种情况下,我认为使用 XmlReader 手动选择文件的时间会更重要障碍)。
看起来您正在使用 C# 和 .NET 3.5。我遇到过一些帖子,这些帖子建议在带有 XmlReader 的文件流上使用 yield 类型的算法。
这里有几篇博客文章可以帮助您开始这条道路:
不确定您正在执行哪种类型的处理,但是对于非常大的 XML,我一直是基于事件的处理的粉丝。也许这是我的 Java 背景,但我真的很喜欢 SAX。您需要进行自己的状态管理,但是一旦您完成了这些,它就会成为解析 XML 的一种非常有效的方法。
我要和youphoric一起去这个。对于非常大的文件,SAX(或任何其他流解析器)将在处理中提供很大帮助。使用 DOM,您可以只收集顶级节点,但您仍然必须解析整个文档才能做到这一点……使用流式解析器和基于事件的处理可以让您“跳过”您不感兴趣的节点;使处理速度更快。
我制作了一个 YouTube 视频,展示了如何使用foxe (来自Firstobject的免费 XML 编辑器)拆分 XML 文件,而不管输入和输出文件的大小如何,只使用少量内存。
此 CMarkup XML 读取器(拉式解析器)和 XML 写入器解决方案的内存使用量取决于从输入文件单独传输到输出文件的子文档的大小,或 16 KB 的最小块大小。
分裂() { CMarkup xmlInput、xmlOutput; xmlInput.Open("50MB.xml", MDF_READFILE); int nObjectCount = 0, nFileCount = 0; 而 ( xmlInput.FindElem("//ACT") ) { 如果(nObjectCount == 0) { ++n文件计数; xmlOutput.Open("piece" + nFileCount + ".xml", MDF_WRITEFILE ); xmlOutput.AddElem("root"); xmlOutput.IntoElem(); } xmlOutput.AddSubDoc(xmlInput.GetSubDoc()); ++n对象计数; 如果(nObjectCount == 5) { xmlOutput.Close(); nObjectCount = 0; } } 如果(nObjectCount) xmlOutput.Close(); xmlInput.Close(); 返回 nFileCount; }