如何将XML文档拆分为三份(甚至更好的是n个)?

时间:2020-03-05 18:38:58  来源:igfitidea点击:

我想使用一种我熟悉的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;
}