Как я могу разбить XML-документ на три части (или, что еще лучше, n частей)?

Я хотел бы использовать знакомый язык - Java, C#, Ruby, PHP, C / C++, хотя примеры на любом языке или псевдокоде более чем приветствуются.

Как лучше всего разбить большой XML-документ на более мелкие разделы, которые по-прежнему являются допустимым XML? Для моих целей мне нужно разделить их примерно на трети или четверти, но для того, чтобы предоставить примеры, было бы хорошо разделить их на n компонентов.

Ответов (10)

Решение

Конечно, вы всегда можете извлечь элементы верхнего уровня (желаемая степень детализации зависит от вас). В 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 (включая его сохранение. обратно на диск и т. д.).

Если у вас нет полной аллергии на Perl, тогда XML :: Twig поставляется с инструментом под названием xml_split, который может разбивать документ, создавая правильно сформированный раздел XML. Вы можете разделить на уровне дерева, по размеру или по выражению XPath.

Анализ XML-документов с использованием DOM не масштабируется.

Этот Groovy -скрипт использует StAX (Streaming API for XML) для разделения XML-документа между элементами верхнего уровня (которые имеют то же QName, что и первый дочерний элемент корневого документа). Это довольно быстро, обрабатывает произвольные большие документы и очень полезно, когда вы хотите разделить большой пакетный файл на более мелкие части.

Требуется Groovy на Java 6 или StAX API и реализация, такая как Woodstox, в CLASSPATH

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()

Это больше комментарий, чем ответ, но не будет:

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

Прочитать сразу весь файл? Просто подумал, что я должен поднять этот вопрос, поскольку, судя по вопросу Томаса, он обеспокоен чтением больших файлов и хочет нарушить процесс ..

Он прочитал бы весь файл сразу. По моему опыту, однако, если вы просто читаете файл, выполняете некоторую обработку (то есть разбиваете его), а затем продолжаете свою работу, XmlDocument будет проходить цикл создания / чтения / сбора так быстро, что это, вероятно, не имеет значения.

Конечно, это зависит от того, что такое «большой» файл. Если это XML-файл размером 30 МБ (который я бы считал большим для XML-файла), это, вероятно, не будет иметь никакого значения. Если это XML-файл размером 500 МБ, использование XmlDocument станет чрезвычайно проблематичным в системах без значительного объема ОЗУ (в этом случае, однако, я бы сказал, что время ручного выбора файла с помощью XmlReader было бы более значительным. препятствие).

Как отмечает здесь ДэнниСмурф, все дело в структуре 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.

Не уверен, какой тип обработки вы выполняете, но для очень больших XML я всегда был поклонником обработки на основе событий. Может быть, это мой опыт работы с Java, но мне действительно нравится SAX. Вам нужно самостоятельно управлять состоянием, но как только вы это преодолеете, это очень эффективный метод синтаксического анализа XML.

http://saxdotnet.sourceforge.net/

Я собираюсь поддержать вас в этом вопросе. Для очень больших файлов SAX (или любой другой потоковый парсер) будет большим подспорьем в обработке. Используя DOM, вы можете собирать только узлы верхнего уровня, но вам все равно придется анализировать весь документ, чтобы сделать это ... использование потокового парсера и обработки на основе событий позволяет вам «пропускать» узлы, которые вам не интересны; ускоряет обработку.

Похоже, вы работаете с C# и .NET 3.5. Я наткнулся на несколько сообщений, в которых предлагается использовать алгоритм типа yield в файловом потоке с помощью XmlReader.

Вот пара сообщений в блоге, которые помогут вам начать свой путь:

Я сделал видео на YouTube, показывающее, как разделить файлы XML с помощью foxe (бесплатного редактора XML от Firstobject ), используя лишь небольшой объем памяти, независимо от размера входных и выходных файлов.

Использование памяти для этого решения CMarkup XML для чтения (синтаксический анализатор) и записи XML зависит от размера вложенных документов, которые индивидуально передаются из входного файла в выходные файлы, или от минимального размера блока 16 КБ.

расколоть()
{
  CMarkup xmlInput, xmlOutput;
  xmlInput.Open («50 МБ.xml», MDF_READFILE);
  int nObjectCount = 0, nFileCount = 0;
  while (xmlInput.FindElem ("// ДЕЙСТВИЕ"))
  {
    если (nObjectCount == 0)
    {
      ++ nFileCount;
      xmlOutput.Open («кусок» + nFileCount + «.xml», MDF_WRITEFILE);
      xmlOutput.AddElem («корень»);
      xmlOutput.IntoElem ();
    }
    xmlOutput.AddSubDoc (xmlInput.GetSubDoc ());
    ++ nObjectCount;
    если (nObjectCount == 5)
    {
      xmlOutput.Close ();
      nObjectCount = 0;
    }
  }
  если (nObjectCount)
    xmlOutput.Close ();
  xmlInput.Close ();
  return nFileCount;
}