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

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

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

Ответы [ 10 ]

5 голосов
/ 24 ноября 2008

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

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

Требуется Groovy на Java 6 или API StAX и реализация, такая как 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()
3 голосов
/ 11 августа 2008

Поскольку 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-файла.

3 голосов
/ 11 августа 2008

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

1 голос
/ 11 августа 2008

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

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

1 голос
/ 11 августа 2008

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

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

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

0 голосов
/ 19 июня 2009

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

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

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;
}
0 голосов
/ 16 сентября 2008

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

0 голосов
/ 16 сентября 2008

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

0 голосов
/ 16 сентября 2008

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

http://saxdotnet.sourceforge.net/

0 голосов
/ 16 сентября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...