Как мне разбить большой XML-файл? - PullRequest
7 голосов
/ 01 декабря 2010

Мы экспортируем «записи» в XML-файл; один из наших клиентов пожаловался на то, что файл слишком велик для обработки его другой системой. Поэтому мне нужно разделить файл, повторяя «заголовок раздела» в каждом из новых файлов.

Итак, я ищу что-то, что позволило бы мне определить некоторые xpath для разделов, которые должны всегда выводиться, и еще один xpath для «строк» ​​с параметром, который говорит, сколько строк помещать в каждый файл как назвать файлы.

Прежде чем я начну писать собственный код .net для этого; есть ли стандартный инструмент командной строки, который будет работать на окнах, которые это делают ?

(Поскольку я знаю, как программировать на C #, я более склонен писать код, а затем пытаться возиться со сложным xsl и т. Д., Но решение "из себя" лучше, чем пользовательский код.)

Ответы [ 7 ]

3 голосов
/ 30 августа 2014

Сначала загрузите foxe xml editor по этой ссылке http://www.firstobject.com/foxe242.zip

Посмотрите это видео http://www.firstobject.com/xml-splitter-script-video.htm Видео объясняет, как работает разделенный код.

На этой странице есть код сценария(начинается с split()) скопируйте код и в программе редактора xml создайте «New Program» под «File».Вставьте код и сохраните его.Код:

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

Измените поля, выделенные жирным шрифтом (или ** **), в соответствии с вашими потребностями.(это также выражено на странице с видео)

В окне редактора xml щелкните правой кнопкой мыши и выберите RUN (или просто F9).В окне есть строка вывода, в которой показано количество сгенерированных файлов.

Примечание: имя входного файла может быть "C:\\Users\\AUser\\Desktop\\a_xml_file.xml" (двойная косая черта), а выходной файл "C:\\Users\\AUser\\Desktop\\anoutputfolder\\piece" + nFileCount + ".xml"

3 голосов
/ 01 декабря 2010

Универсального решения для этого не существует, потому что существует так много разных возможных способов структурирования вашего исходного XML.

Достаточно просто построить XSLT-преобразование, которое будет выводить фрагмент XML-документа. Например, учитывая этот XML:

<header>
  <data rec="1"/>
  <data rec="2"/>
  <data rec="3"/>
  <data rec="4"/>
  <data rec="5"/>
  <data rec="6"/>
</header>

вы можете вывести копию файла, содержащего только data элементов в определенном диапазоне, с помощью этого XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:param name="startPosition"/>
  <xsl:param name="endPosition"/>

  <xsl:template match="@* | node()">
      <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
      </xsl:copy> 
  </xsl:template>

  <xsl:template match="header">
    <xsl:copy>
      <xsl:apply-templates select="data"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="data">
    <xsl:if test="position() &gt;= $startPosition and position() &lt;= $endPosition">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

(Заметьте, кстати, что, поскольку это основано на преобразовании идентичности, оно работает, даже если header не является элементом верхнего уровня.)

Вам все еще нужно сосчитать элементы data в исходном XML и повторно выполнить преобразование со значениями $startPosition и $endPosition, которые соответствуют ситуации.

2 голосов
/ 22 сентября 2015

Как уже упоминалось, xml_split из пакета Perl XML :: Twig отлично справляется с работой.

Использование

xml_split < bigFile.xml

#or if compressed e.g.
bzcat bigFile.xml.bz2 | xml_split

Без аргументов xml_split создает файл для каждого дочернего узла верхнего уровня.

Есть параметры для указания количества элементов, которое вы хотите для файла (-g) или приблизительный размер(-s <Kb|Mb|Gb>).

Установка

Windows

Смотрите здесь

Linux

sudo apt-get install xml-twig-tools

2 голосов
/ 19 октября 2011

xml_split - разбить огромные XML-документы на более мелкие куски

http://www.perlmonks.org/index.pl?node_id=429707

http://metacpan.org/pod/XML::Twig

1 голос
/ 01 декабря 2010

Нет ничего встроенного, что может легко справиться с этой ситуацией.

Ваш подход звучит разумно, хотя я, вероятно, начну с "скелетного" документа, содержащего элементы, которые необходимо повторить, и сгенерирую несколько документов с "записями".


Обновление:

После небольшой копки я нашел эту статью , описывающую способ разделения файлов с использованием XSLT.

0 голосов
/ 11 июня 2015

Использование Ultraedit на основе https://www.ultraedit.com/forums/viewtopic.php?f=52&t=6704

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

    // from https://www.ultraedit.com/forums/viewtopic.php?f=52&t=6704 

var FoundsPerFile = 200;      // Global setting for number of found split strings per file.
var SplitString = "</letter>";  // String where to split. The split occurs after next character.
var xmlHead = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
var xmlRootStart = '<letters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" letterCode="OA01" >';
var xmlRootEnd = '</letters>';

/* Find the tab index of the active document */
// Copied from http://www.ultraedit.com/forums/viewtopic.php?t=4571
function getActiveDocumentIndex () {
   var tabindex = -1; /* start value */

   for (var i = 0; i < UltraEdit.document.length; i++)
   {
      if (UltraEdit.activeDocument.path==UltraEdit.document[i].path) {
         tabindex = i;
         break;
      }
   }
   return tabindex;
}

if (UltraEdit.document.length) { // Is any file open?
   // Set working environment required for this job.
   UltraEdit.insertMode();
   UltraEdit.columnModeOff();
   UltraEdit.activeDocument.hexOff();
   UltraEdit.ueReOn();

   // Move cursor to top of active file and run the initial search.
   UltraEdit.activeDocument.top();
   UltraEdit.activeDocument.findReplace.searchDown=true;
   UltraEdit.activeDocument.findReplace.matchCase=true;
   UltraEdit.activeDocument.findReplace.matchWord=false;
   UltraEdit.activeDocument.findReplace.regExp=false;
   // If the string to split is not found in this file, do nothing.
   if (UltraEdit.activeDocument.findReplace.find(SplitString)) {
      // This file is probably the correct file for this script.
      var FileNumber = 1;    // Counts the number of saved files.
      var StringsFound = 1;  // Counts the number of found split strings.
      var NewFileIndex = UltraEdit.document.length;
      /* Get the path of the current file to save the new
         files in the same directory as the current file. */
      var SavePath = "";
      var LastBackSlash = UltraEdit.activeDocument.path.lastIndexOf("\\");
      if (LastBackSlash >= 0) {
         LastBackSlash++;
         SavePath = UltraEdit.activeDocument.path.substring(0,LastBackSlash);
      }
      /* Get active file index in case of more than 1 file is open and the
         current file does not get back the focus after closing the new files. */
      var FileToSplit = getActiveDocumentIndex();
      // Always use clipboard 9 for this script and not the Windows clipboard.
      UltraEdit.selectClipboard(9);
      // Split the file after every x found split strings until source file is empty.
      while (1) {
         while (StringsFound < FoundsPerFile) {
            if (UltraEdit.document[FileToSplit].findReplace.find(SplitString)) StringsFound++;
            else {
               UltraEdit.document[FileToSplit].bottom();
               break;
            }
         }
         // End the selection of the find command.
         UltraEdit.document[FileToSplit].endSelect();
         // Move the cursor right to include the next character and unselect the found string.
         UltraEdit.document[FileToSplit].key("RIGHT ARROW");
         // Select from this cursor position everything to top of the file.
         UltraEdit.document[FileToSplit].selectToTop();
         // Is the file not already empty?
         if (UltraEdit.document[FileToSplit].isSel()) {
            // Cut the selection and paste it into a new file.
            UltraEdit.document[FileToSplit].cut();
            UltraEdit.newFile();
            UltraEdit.document[NewFileIndex].setActive();
            UltraEdit.activeDocument.paste();


            /* Add line termination on the last line and remove automatically added indent
               spaces/tabs if auto-indent is enabled if the last line is not already terminated. */
            if (UltraEdit.activeDocument.isColNumGt(1)) {
               UltraEdit.activeDocument.insertLine();
               if (UltraEdit.activeDocument.isColNumGt(1)) {
                  UltraEdit.activeDocument.deleteToStartOfLine();
               }
            }

            // add headers and footers 

            UltraEdit.activeDocument.top();
            UltraEdit.activeDocument.write(xmlHead);
                        UltraEdit.activeDocument.write(xmlRootStart);
            UltraEdit.activeDocument.bottom();
            UltraEdit.activeDocument.write(xmlRootEnd);
            // Build the file name for this new file.
            var SaveFileName = SavePath + "LETTER";
            if (FileNumber < 10) SaveFileName += "0";
            SaveFileName += String(FileNumber) + ".raw.xml";
            // Save the new file and close it.
            UltraEdit.saveAs(SaveFileName);
            UltraEdit.closeFile(SaveFileName,2);
            FileNumber++;
            StringsFound = 0;
            /* Delete the line termination in the source file
               if last found split string was at end of a line. */
            UltraEdit.document[FileToSplit].endSelect();
            UltraEdit.document[FileToSplit].key("END");
            if (UltraEdit.document[FileToSplit].isColNumGt(1)) {
               UltraEdit.document[FileToSplit].top();
            } else {
               UltraEdit.document[FileToSplit].deleteLine();
            }
         } else break;
            UltraEdit.outputWindow.write("Progress " + SaveFileName);
      }  // Loop executed until source file is empty!

      // Close source file without saving and re-open it.
      var NameOfFileToSplit = UltraEdit.document[FileToSplit].path;
      UltraEdit.closeFile(NameOfFileToSplit,2);
      /* The following code line could be commented if the source
         file is not needed anymore for further actions. */
      UltraEdit.open(NameOfFileToSplit);

      // Free memory and switch back to Windows clipboard.
      UltraEdit.clearClipboard();
      UltraEdit.selectClipboard(0);
   }
}
0 голосов
/ 18 декабря 2010

"есть ли стандартный инструмент командной строки, который будет работать на окнах, которые это делают?"

Да.http://xponentsoftware.com/xmlSplit.aspx

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