xslt сортировка выходного xml

#xslt #sorting #xslt-2.0

#xslt #сортировка #xslt-2.0

Вопрос:

Я пытаюсь найти решение следующей проблемы.

Я разрабатываю преобразование XSLT (которое сейчас имеет размер около 40 КБ), которое преобразует довольно сложные XML-файлы в довольно простую структуру, которая хотела бы этого:

 <Records>
<Record key="XX">
</Record> 
<Record key="XX1">
</Record>
<Record key="XX2">
</Record>
<Record key="XX3">
</Record>
</Records>
  

Я хотел бы, чтобы этот выходной XML был отсортирован по Records/Record/@key значениям.
Проблема в том, что мой XSLT выдает этот вывод без сортировки, и из-за его сложности я не могу отсортировать его там.
Можно ли применить xsl:sort к выходному XML? Я знаю, что могу подготовить другое преобразование XSLT, но в моем случае это не решение, поскольку я ограничен только одним XSLT .. Пожалуйста, помогите !…

Ответ №1:

Можно ли применить xsl:sort к выходному XML?

Да, многопроходная обработка возможна, и особенно в XSLT 2.0 вам даже не нужно применять xxx:node-set() расширение к результату, потому что печально известный тип RTF больше не существует:

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

 <xsl:template match="/">
  <xsl:variable name="vPass1">
   <!--
        Put/Invoke your cirrent code here   
        to generate the following           
-->
    <Records>
      <Record key="XX3">
      </Record>
      <Record key="XX2">
      </Record>
      <Record key="XX4">
      </Record>
      <Record key="XX1">
      </Record>
    </Records>
  </xsl:variable>

  <xsl:apply-templates select="$vPass1/*"/>
 </xsl:template>

 <xsl:template match="Records">
  <Records>
   <xsl:perform-sort select="*">
    <xsl:sort select="@key"/>
   </xsl:perform-sort>
  </Records>
 </xsl:template>
</xsl:stylesheet>
  

Когда это преобразование выполняется для любого XML-документа (не используется / игнорируется), создается требуемый, правильный, отсортированный результат:

 <Records>
   <Record key="XX1"/>
   <Record key="XX2"/>
   <Record key="XX3"/>
   <Record key="XX4"/>
</Records>
  

В XSLT 1.0 это почти то же самое с дополнительным преобразованием результата из RTF-типа в обычное дерево:

 <xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <!--
        Put/Invoke your cirrent code here   
        to generate the following           
-->
    <Records>
      <Record key="XX3">
      </Record>
      <Record key="XX2">
      </Record>
      <Record key="XX4">
      </Record>
      <Record key="XX1">
      </Record>
    </Records>
  </xsl:variable>

  <xsl:variable name="vPass1"
                select="ext:node-set($vrtfPass1)"/>

  <xsl:apply-templates select="$vPass1/*"/>
 </xsl:template>

 <xsl:template match="Records">
  <Records>
   <xsl:for-each select="*">
    <xsl:sort select="@key"/>

    <xsl:copy-of select="."/>
   </xsl:for-each>
  </Records>
 </xsl:template>
</xsl:stylesheet>
  

Комментарии:

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

2. @LukaszBaran: Как вы думаете, как я создал этот код? Я всегда тестирую свой код перед его публикацией. На самом деле, то, что вы видите как «результат», всегда копируется и вставляется из фактического результата в моем тесте. Если у вас возникли какие-либо проблемы при выполнении первого решения, это, скорее всего, означает, что вы не запускали его с процессором XSLT 2.0. Найдите себе один и попробуйте еще раз. Также возможно, что вы заменили комментарий своим кодом, и в вашем коде есть шаблоны, соответствующие тем же узлам (например, document-node() ).). Всегда безопаснее поместить весь код для данного прохода в именованный режим, например «mPass1».

3. Правильно. Я думаю, что это было вызвано Xalan, который я использую для преобразования XSLT.

4. @LukaszBaran: Итак, в этом случае вы должны использовать решение XSLT 1.0. Если EXSLT недоступен, используйте встроенную функцию расширения Xalan xxx:nodeset()

5. Хорошо, это работает! Я применил последнюю версию Saxon. Большое спасибо, 1, и я принимаю это решение.

Ответ №2:

40 КБ — это много кода для одной таблицы стилей. Когда дело доходит до такого масштаба, обычно лучше разделить преобразование на конвейер меньших преобразований. Если у вас такая конвейерная архитектура, то добавление шага сортировки в конце является тривиальным. Существует множество технологий для управления конвейером преобразований (XProc, Orbeon, xmlsh, ant, Coccoon) в зависимости от ваших требований. Преимущество конвейерной обработки заключается в том, что она сохраняет ваш код модульным и пригодным для повторного использования.

Ответ №3:

В качестве дополнения к превосходному решению Dimitre, описанному выше, если вы используете процессор XSLT 1.0 (например, .NET), следующее может дать вам указатель на то, как использовать node-set: http://www.xml.com/pub/a/2003/07/16/nodeset.html#tab.namespaces

В моем случае я был в .NET 1.1 (т. Е. MSXML), и решение выглядело примерно так:

 <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <xsl:template match="/">            
        <xsl:variable name="vrtfPass1">
            <Records xmlns="">  
                    <xsl:apply-templates />
            </Records >            
        </xsl:variable>
        <xsl:variable name="vPass1" select="msxsl:node-set($vrtfPass1)"/>
        <xsl:apply-templates select="$vPass1/*" mode="sorting"/>
    </xsl:template>
    <xsl:template match="Records" mode="sorting">
        <Records>
       <xsl:for-each select="Record">
        <xsl:sort select="@key"/>
        <xsl:copy-of select="."/>
       </xsl:for-each>
      </Records>
    </xsl:template>
</xsl:stylesheet>