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

StackOverflow https://stackoverflow.com/questions/7846

  •  08-06-2019
  •  | 
  •  

Вопрос

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

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

Другие советы

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

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

Требуется Groovy на Java 6 или StAX API и такая реализация, как Вудстокс в ПУТИ к КЛАССУ

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

Как здесь упоминает 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 doc = new XmlDocument();
doc.Load("path");

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

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

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

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

http://saxdotnet.sourceforge.net/

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

Если у вас нет полной аллергии на Perl, то XML::Веточка поставляется с инструментом под названием xml_split ( xml_split ) это может разделить документ, создав хорошо сформированный XML-раздел.Вы можете разделить дерево по уровню, размеру или по выражению XPath.

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

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

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

Использование памяти для этого решения CMarkup XML reader (pull parser) и XML writer зависит от размера вложенных документов, которые по отдельности передаются из входного файла в выходные файлы, или минимального размера блока 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top