XSLT 1.0: ограничить записи в кишечнике
-
09-10-2019 - |
Вопрос
Быть относительно новым для 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>