如何将XML文档拆分为三分之一(或者更好的是,n个部分)?

时间:2008-08-11 15:04:21

标签: xml

我想使用我熟悉的语言 - Java,C#,Ruby,PHP,C / C ++,尽管任何语言或伪代码的示例都非常受欢迎。

将大型XML文档拆分为仍然有效的XML的较小部分的最佳方法是什么?为了我的目的,我需要将它们分成大约三分之二或四分之一,但为了提供示例,将它们分成n个组件会很好。

10 个答案:

答案 0 :(得分:5)

使用DOM解析XML文档无法扩展。

这个Groovy - 脚本使用StAX(Streaming API for XML)在顶层元素(与根文档的第一个子节点共享相同的QName)之间拆分XML文档。它非常快,处理任意大型文档,当你想将一个大批量文件拆分成更小的文件时非常有用。

需要在Java 6上使用Groovy或StAX API以及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()

答案 1 :(得分:3)

当然,您总是可以提取顶级元素(这是否是您想要的粒度取决于您)。在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片段(就像它一样)包括将其保存回磁盘等。)

答案 2 :(得分:3)

正如DannySmurf所说,这完全是关于xml文档的结构 如果你只有两个巨大的“顶级”标签,那么能够以一种可以将它重新合并并将其作为有效的xml一块一块地读取的方式进行拆分将非常困难。

鉴于一个包含许多单独部分的文档,例如DannySmurfs示例中的文档,它应该相当容易。
Pseudo 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的文档以及将它们合并在一起的可能性 但同样,它取决于xml文件。

答案 3 :(得分:1)

这更多的是评论而不是答案,但不会:

XmlDocument doc = new XmlDocument();
doc.Load("path");

立即阅读整个文件?刚认为我应该提出这一点,因为从托马斯的问题来看,他担心阅读大文件并想要打破这个过程。

答案 4 :(得分:1)

它会立即读取整个文件。但是,根据我的经验,如果你只是阅读文件,进行一些处理(即分解)然后再继续你的工作,那么XmlDocument将会如此迅速地完成创建/读取/收集循环。它可能无关紧要。

当然,这取决于“大”文件是什么。如果它是一个30 MB的XML文件(我认为XML文件很大),它可能没有任何区别。如果它是一个500 MB的XML文件,使用XmlDocument将在没有大量RAM的系统上变得非常有问题(但是,在这种情况下,我认为用XmlReader手动选择文件的时间会更重要障碍)。

答案 5 :(得分:0)

看起来你正在使用C#和.NET 3.5。我发现了一些帖子,建议在带有XmlReader的文件流上使用yield类型的算法。

以下是一些博客文章,让您开始走上正轨:

答案 6 :(得分:0)

不确定您正在做什么类型的处理,但对于非常大的XML,我一直是基于事件的处理的粉丝。也许这是我的Java背景,但我确实喜欢SAX。您需要进行自己的状态管理,但是一旦超过它,它就是一种非常有效的解析XML的方法。

http://saxdotnet.sourceforge.net/

答案 7 :(得分:0)

我使用how to split XML files(来自foxe的免费XML编辑器)显示Firstobject的YouTube视频,只使用少量内存,无论输入和输出文件的大小如何。

此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;
}

答案 8 :(得分:0)

我会在这个问题上和你一起去。对于非常大的文件,SAX(或任何其他流式解析器)将在处理方面提供很大帮助。使用DOM,您只能收集顶级节点,但您仍需要解析整个文档才能执行此操作...使用流式解析器和基于事件的处理可让您“跳过”您不感兴趣的节点;使处理更快。

答案 9 :(得分:0)

如果您对Perl没有完全过敏,那么XML::Twig会附带一个名为xml_split的工具,可以拆分文档,生成格式良好的XML部分。您可以按树的大小,按大小或XPath表达式进行拆分。