Вопрос

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

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

И это должно быть выглядеть так:

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

У меня есть рабочее XSLT для этого, и это, по сути, получает odsetet всех элементов B, а затем использует осью «следующий брат», чтобы получить кишеватель узлов, следующих за текущим узлом B (т. Е. После родного брата :: * [Положение () = $ Nodepos]). Затем рекурсион используется для добавления братьев и сестер в дереве результата до тех пор, пока не найден другой элемент B (я, конечно, параметризовал, чтобы сделать его более универсальным).

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

Проблема в том, что время выполнения преобразования недопустимо увеличивается с размером файла XML. Глядя к нему с шпионом XML, кажется, что это «следующий брат» и «позиция () =», которое требует времени в двух соответствующих методах.

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

Спасибо,

Майк

Это было полезно?

Решение

Да, есть способ сделать это намного эффективнее: см. Muenchian Grouping. Отказ Если посмотрим на это, вам нужно больше помочь с деталями, дайте нам знать. Ключ, который вам понадобится, это что-то вроде:

<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 имеет очень полезные функции группировки, которые легче использовать, чем Munenchian метод.

В вашем случае это звучит так, как будто вы бы использовали <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>

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

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

Это мелкозернистый трасберссный узор:

<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>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top