Сортировка XML с использованием XSLT - PullRequest
1 голос
/ 21 апреля 2011

У меня есть XML, который описывает дерево каталогов.Может иметь любое количество дочерних узлов.Вот пример:

<Catalog name="AccessoriesCatalog">
<Category Definition="AccessoriesCategory" name="1532" id="1532">
</Category>
<Category Definition="AccessoriesCategory" name="16115" id="16115">
    <ParentCategory>1532</ParentCategory>
</Category>
<Category Definition="AccessoriesCategory" name="16116" id="16116">
    <ParentCategory>16115</ParentCategory>
</Category>
<Category Definition="AccessoriesCategory" name="16126" id="16126">
    <ParentCategory>16115</ParentCategory>
</Category>
<Category Definition="AccessoriesCategory" name="16131" id="16131">
    <ParentCategory>1532</ParentCategory>
</Category>
<Category Definition="AccessoriesCategory" name="16132" id="16132">
    <ParentCategory>16131</ParentCategory>
</Category>
<Category Definition="AccessoriesCategory" name="16136" id="16136">
    <ParentCategory>16131</ParentCategory>
</Category>
<Category Definition="AccessoriesCategory" name="16139" id="16139">
    <ParentCategory>16131</ParentCategory>
</Category>
<Category Definition="AccessoriesCategory" name="16144" id="16144">
    <ParentCategory>16131</ParentCategory>
</Category>
<Category Definition="AccessoriesCategory" name="16195" id="16195">
    <ParentCategory>16131</ParentCategory>
</Category>

Мне нужно иметь возможность сортировать его по названию категории и ParentCategory.Все родительские категории должны быть первыми в xml, а «конечные категории» должны быть последними.В этом примере xml уже отсортирован.

Приведенный выше xml выглядит следующим образом, когда он представлен в виде дерева
1532
-16115
--16116
--16126
-16131
--16132
--16136
--16139
--16144
--16195

Я хочу, чтобы он был отсортирован следующим образом
1532
-16115
-16131
--16116
--16126
--16132
--16136
--16139
--16144
--16195

Это может быть несколько уровней дочерних элементов (в данном случае только трехуровневое дерево).Я хочу, чтобы все элементы уровня 1 были первыми в xml, затем все элементы уровня 2, а затем все элементы уровня 3 и т. Д.

Ответы [ 2 ]

2 голосов
/ 22 апреля 2011

Это преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kElemById" match="Category"
  use="@id"/>

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

 <xsl:template match="/*">
  <xsl:copy>
   <xsl:copy-of select="@*"/>

   <xsl:call-template name="sortHier">
    <xsl:with-param name="pNodes" select=
    "*[ParentCategory]"/>
    <xsl:with-param name="pParents" select=
    "*[not(ParentCategory)]"/>
   </xsl:call-template>
  </xsl:copy>
 </xsl:template>

 <xsl:template name="sortHier">
  <xsl:param name="pNodes"/>
  <xsl:param name="pParents"/>

  <xsl:apply-templates select=
   "$pParents|$pNodes[not($pParents)]">
   <xsl:sort select="@name"/>
  </xsl:apply-templates>

   <xsl:if test="$pNodes and $pParents">
    <xsl:variable name="vNewParents"
     select="key('kElemById', $pNodes/ParentCategory)
                [not(@id=$pParents/@id)]
     "/>

    <xsl:variable name="vNewChildren"
     select="$pNodes[not(@id=$vNewParents/@id)]"/>

    <xsl:call-template name="sortHier">
     <xsl:with-param name="pNodes"
          select="$vNewChildren"/>
     <xsl:with-param name="pParents"
          select="$vNewParents"/>
    </xsl:call-template>
   </xsl:if>
 </xsl:template>
</xsl:stylesheet>

при применении к этому документу XML (на основе предоставленного, но перемешанного / несортированного):

<Catalog name="AccessoriesCatalog">
    <Category Definition="AccessoriesCategory"
    name="16144" id="16144">
        <ParentCategory>16131</ParentCategory>
    </Category>
    <Category Definition="AccessoriesCategory"
    name="16116" id="16116">
        <ParentCategory>16115</ParentCategory>
    </Category>
    <Category Definition="AccessoriesCategory"
    name="16126" id="16126">
        <ParentCategory>16115</ParentCategory>
    </Category>
    <Category Definition="AccessoriesCategory"
    name="16131" id="16131">
        <ParentCategory>1532</ParentCategory>
    </Category>
    <Category Definition="AccessoriesCategory"
    name="16132" id="16132">
        <ParentCategory>16131</ParentCategory>
    </Category>
    <Category Definition="AccessoriesCategory"
    name="16136" id="16136">
        <ParentCategory>16131</ParentCategory>
    </Category>
    <Category Definition="AccessoriesCategory"
    name="16139" id="16139">
        <ParentCategory>16131</ParentCategory>
    </Category>
    <Category Definition="AccessoriesCategory"
    name="16115" id="16115">
        <ParentCategory>1532</ParentCategory>
    </Category>
    <Category Definition="AccessoriesCategory"
    name="1532" id="1532"></Category>
    <Category Definition="AccessoriesCategory"
    name="16195" id="16195">
        <ParentCategory>16131</ParentCategory>
    </Category>
</Catalog>

дает желаемый, правильный результат :

<Catalog name="AccessoriesCatalog">
   <Category Definition="AccessoriesCategory" name="1532" id="1532"/>
   <Category Definition="AccessoriesCategory" name="16115" id="16115">
      <ParentCategory>1532</ParentCategory>
   </Category>
   <Category Definition="AccessoriesCategory" name="16131" id="16131">
      <ParentCategory>1532</ParentCategory>
   </Category>
   <Category Definition="AccessoriesCategory" name="16116" id="16116">
      <ParentCategory>16115</ParentCategory>
   </Category>
   <Category Definition="AccessoriesCategory" name="16126" id="16126">
      <ParentCategory>16115</ParentCategory>
   </Category>
   <Category Definition="AccessoriesCategory" name="16132" id="16132">
      <ParentCategory>16131</ParentCategory>
   </Category>
   <Category Definition="AccessoriesCategory" name="16136" id="16136">
      <ParentCategory>16131</ParentCategory>
   </Category>
   <Category Definition="AccessoriesCategory" name="16139" id="16139">
      <ParentCategory>16131</ParentCategory>
   </Category>
   <Category Definition="AccessoriesCategory" name="16144" id="16144">
      <ParentCategory>16131</ParentCategory>
   </Category>
   <Category Definition="AccessoriesCategory" name="16195" id="16195">
      <ParentCategory>16131</ParentCategory>
   </Category>
</Catalog>

Объяснение

  1. Рекурсивно вызванный именованный шаблон с двумя параметрами: «набор текущих родителей» (или «последний найденный родитель») и набор текущих (все еще не обработанных) узлов.

  2. Условие останова : либо «набор текущих родителей», либо «набор текущих узлов», либо оба они пусты. Здесь мы выводим (и сортируем по @name) оставшийся непустой набор параметров.

  3. Рекурсивный шаг : непосредственные дети «нынешних родителей» становятся новыми «текущими родителями». Остальные «текущие узлы» становятся новыми «текущими узлами». Скопируйте все текущие родительские элементы или все текущие узлы, если не осталось текущих родительских узлов.

Обновление

В комментариях OP заявляет, что решение работало с небольшими файлами,

"Но когда я пробую это на весь xml с большим количеством элементов и уровней не работает. XML у меня есть около 8 Мб, поэтому я не могу опубликовать это здесь. "

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

Производительность файла большего размера была неплохой , за исключением MSXML3.

Вот данные о производительности файла 44000 строк , как видно на моем 8-летнем ПК (2 ГБ ОЗУ, одноядерный 3GHz):

MSXML3:                 91 sec.

MSXML6:                  6 sec.

AltovaXML (XMLSpy):      6 sec.

Saxon 6.5.4:             2 sec.

Saxon 9.1.05:            1.6 sec.

XslCompiledTransform     1.3 sec.

XQSharp:                 0.8 sec.
0 голосов
/ 21 апреля 2011

Я думаю, что ваш ответ можно найти здесь: http://www.programmersheaven.com/2/FAQ-XML-Sort-XML-By-Multiple-Attributes-XSLT

...