XSLT 1.0: ограничить записи в наборе узлов - PullRequest
1 голос
/ 10 января 2011

Будучи относительно новым для XSLT, я надеюсь, что это простой вопрос. У меня есть несколько плоских XML-файлов, которые могут быть довольно большими (например, 7 МБ), которые мне нужно сделать «более иерархическими». Например, плоский XML может выглядеть так:

<D0011>
    <b/>
    <c/>
    <d/>
    <e/>
    <b/>
    ....
    ....
</D0011>

и должно получиться так:

<D0011>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
  <b>
 ....
 ....
</D0011>

У меня есть рабочий XSLT для этого, и он, по сути, получает набор узлов всех элементов b, а затем использует ось 'follow-sibling', чтобы получить набор узлов, следующих за текущим узлом b (т.е. :: * [положение () = $ nodePos]). Затем используется рекурсия для добавления братьев и сестер в дерево результатов до тех пор, пока не будет найден другой элемент b (я, конечно, его параметризовал, чтобы сделать его более общим).

У меня также есть решение, которое просто отправляет позицию в XML следующего узла b и выбирает узлы после этого один за другим (с использованием рекурсии) с помощью выбора * [position () = $ nodePos].

Проблема в том, что время выполнения преобразования недопустимо увеличивается с размером файла XML. Рассматривая это с помощью XML Spy, кажется, что это «follow-sibling» и «position () =», которые занимают время в двух соответствующих методах.

Что мне действительно нужно, так это способ ограничения числа узлов в вышеупомянутых выборках, поэтому выполняется меньше сравнений: каждый раз, когда проверяется позиция, каждый узел в наборе узлов проверяется, чтобы определить, является ли его позиция правильной. , Есть способ сделать это ? Любые другие предложения?

Спасибо

Mike

Ответы [ 3 ]

1 голос
/ 10 января 2011
<xsl:key name="k1" match="D0011/*[not(self::b)]" use="generate-id(preceding-sibling::b[1])"/>

<xsl:template match="D0011">
  <xsl:copy>
    <xsl:apply-templates select="b"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="D0011/b">
  <xsl:copy>
    <xsl:copy-of select="key('k1', generate-id())"/>
  </xsl:copy>
</xsl:template>
1 голос
/ 10 января 2011

Да, есть способ сделать это намного эффективнее: см. Мюнхенская группировка . Если вам нужно больше информации, сообщите нам об этом. Ключ вам понадобится что-то вроде:

<xsl:key name="elements-by-group" match="*[not(self::b)]"
   use="generate-id(preceding-sibling::b[1])" />

Затем вы можете перебрать элементы <b>, и для каждого из них используйте key('elements-by-group', generate-id()), чтобы получить элементы, следующие сразу за этим <b>.

Задача «сделать XML более иерархичным» иногда называется повышающим преобразованием, и ваш сценарий является классическим примером для этого. Как вы, возможно, знаете, XSLT 2.0 имеет очень полезные функции группировки, которые проще в использовании, чем метод Мюнхена.

В вашем случае это звучит так, как будто вы используете <xsl:for-each-group group-starting-with="b" /> или, чтобы параметризовать имя элемента, <xsl:for-each-group group-starting-with="*[local-name() = 'b']" />. Но, возможно, вы уже рассмотрели это и не можете использовать XSLT 2.0 в своей среде.

Обновление:

В ответ на запрос параметризации, вот способ сделать это без ключа. Обратите внимание, что это может быть намного медленнее, в зависимости от вашего процессора XSLT.

<xsl:template match="D0011">
   <xsl:for-each select="*[local-name() = $sep]">
      <xsl:copy>
         <xsl:copy-of select="following-sibling::*[not(local-name() = $sep)
               and generate-id(preceding-sibling::*[local-name() = $sep][1]) =
                    generate-id(current())]" />
      </xsl:copy>
   </xsl:for-each>      
</xsl:template>

Как отмечено в комментарии, вы можете сохранить выигрыш в производительности ключей, задав несколько различных ключей, по одному для каждого возможного значения параметра. Затем вы выбираете, какую клавишу использовать, используя <xsl:choose>.

Обновление 2:

Чтобы элемент, начинающий группу, определялся на основе /*/*[2], а не на основе параметра, используйте

<xsl:key name="elements-by-group"
   match="*[not(local-name(.) = local-name(/*/*[2]))]"
   use="generate-id(preceding-sibling::*
                           [local-name(.) = local-name(/*/*[2])][1])" />

<xsl:template match="D0011">
   <xsl:for-each select="*[local-name(.) = local-name(../*[2])]">
      <xsl:copy>
         <xsl:copy-of select="key('elements-by-group', generate-id())"/>
      </xsl:copy>
   </xsl:for-each>
</xsl:template>
0 голосов
/ 10 января 2011

Это мелкозернистый шаблон прохождения:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="b[1]" name="group">
        <xsl:copy>
            <xsl:apply-templates select="following-sibling::node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::b[1]" mode="group"/>
    </xsl:template>
    <xsl:template match="b[position()!=1]"/>
    <xsl:template match="b" mode="group">
        <xsl:call-template name="group"/>
    </xsl:template>
</xsl:stylesheet>

Выход:

<D0011>
    <b>
        <c></c>
        <d></d>
        <e></e>
    </b>
    <b>
    ....
    ....
    </b>
</D0011>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...