Переупорядочить узлы, возвращаемые из xsl: выбрать
Какой эффективный способ переупорядочить группу узлов, выбранных с помощью xsl:choose (XSLT 1.0).
Ниже приведен пример исходного кода XML:
<Universe>
<CObj>
<Galaxies>
<Galaxy>
<Profile>
<Name>MilkyWay</Name>
<Age>12.5</Age>
</Profile>
<PlanetarySystem>
<Name>Solar</Name>
<Location></Location>
<Planet>
<Name>Earth</Name>
<Satellite>Y</Satellite>
...
...
...
</Planet>
...
...
...
</PlanetarySystem>
<PlanetarySystem>
...
...
...
</PlanetarySystem>
</Galaxy>
<Galaxy>
...
...
...
</Galaxy>
</Galaxies>
</CObj>
</Universe>
Фрагмент XSL:
<xsl:template name="get-galaxy-types">
<xsl:variable name="galaxy_age1" select ="1" />
<xsl:variable name="galaxy_age2" select ="5" />
<xsl:variable name="galaxy_age3" select ="10" />
<xsl:for-each select="Galaxies/Galaxy/Profile/Age">
<xsl:choose>
<xsl:when test=".=$galaxy_age2">
<GalaxyType2>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType2>
</xsl:when>
<xsl:when test=".=$galaxy_age3">
<GalaxyType3>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType3>
</xsl:when>
<xsl:when test=".=$galaxy_age1">
<GalaxyType1>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType1>
</xsl:when>
</xsl:choose>
</xsl:for-each>
Выше шаблон XSL вызывается из основного шаблона, например:
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:call-template name="get-galaxy-types"/>
</GalaxyTypes>
</xsl:template>
Выходной XML: обратите внимание, что порядок <GalaxyType>
не может быть изменено
<Universe>
...
...
...
<GalaxyTypes>
<GalaxyType2>xxxxxx</GalaxyType2>
<GalaxyType3>xxxxxx</GalaxyType3>
<GalaxyType1>xxxxxx</GalaxyType1>
</GalaxyTypes>
...
...
...
</Universe>
поскольку xsl:choose
возвращает узлы XML как и когда он находит совпадение, я не могу найти прямой способ управления порядком, в котором я хочу GalaxyType
появляться в выходном XML.
Как я могу иметь общий шаблон для выполнения переупорядочения для любых элементов, которые могут быть добавлены в будущем, которые могут соответствовать аналогичным требованиям. У меня все в порядке с шаблоном переотображения в этом XSL, но я не совсем уверен, как сделать это действительно элегантным и эффективным способом.
2 ответа
Я предполагаю, что вы хотите поместить сначала галактики, соответствующие galaxy-age1, затем те, которые соответствуют galaxy-age2, и, наконец, те, что для galaxy-age3. Я также предполагаю, что указанные возрасты могут быть не в порядке возрастания (то есть, galaxy-age3 может быть меньше, чем galaxy-age1).
Начнем с того, что было бы более естественным сделать xsl: for-each над элементами Galaxy.
<xsl:for-each select="Galaxies/Galaxy">
Затем, чтобы выполнить настраиваемую сортировку, вы можете сначала определить переменную, например, так...
<xsl:variable name="sortAges"
select="concat('-', $galaxy_age1, '-', $galaxy_age2, '-', $galaxy_age3, '-')" />
Обратите внимание на порядок, в котором параметры отображаются в concat
утверждение соответствует порядку, который они должны быть выведены.
Тогда ваш xsl: for-each может выглядеть вот так...
<xsl:for-each select="Galaxies/Galaxy">
<xsl:sort select="string-length(substring-before($sortAges, concat('-', Profile/Age, '-')))" />
Но это не очень элегантно. Может быть, лучше просто иметь шаблон, который соответствует Galaxy и использовать три отдельных xsl: apply-templates для выбора Galaxy; по одному на каждый возраст.
Попробуйте это XSLT тоже
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:param name="galaxy_age1" select="1" />
<xsl:param name="galaxy_age2" select="5" />
<xsl:param name="galaxy_age3" select="10" />
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age1]">
<xsl:with-param name="num" select="1" />
</xsl:apply-templates>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age2]">
<xsl:with-param name="num" select="2" />
</xsl:apply-templates>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age3]">
<xsl:with-param name="num" select="3" />
</xsl:apply-templates>
</GalaxyTypes>
</xsl:template>
<xsl:template match="Galaxy">
<xsl:param name="num" />
<xsl:element name="Galaxy{$num}">
<xsl:value-of select="Profile/Name"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
РЕДАКТИРОВАТЬ: Чтобы сделать это более эффективным, рассмотрите возможность использования ключа для поиска элементов Galaxy по их названию. Попробуйте это XSLT тоже
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:param name="galaxy_age1" select="1" />
<xsl:param name="galaxy_age2" select="10" />
<xsl:param name="galaxy_age3" select="5" />
<xsl:key name="galaxy" match="Galaxy" use="Profile/Age" />
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:apply-templates select="key('galaxy', $galaxy_age1)">
<xsl:with-param name="num" select="1" />
</xsl:apply-templates>
<xsl:apply-templates select="key('galaxy', $galaxy_age2)">
<xsl:with-param name="num" select="2" />
</xsl:apply-templates>
<xsl:apply-templates select="key('galaxy', $galaxy_age3)">
<xsl:with-param name="num" select="3" />
</xsl:apply-templates>
</GalaxyTypes>
</xsl:template>
<xsl:template match="Galaxy">
<xsl:param name="num" />
<xsl:element name="Galaxy{$num}">
<xsl:value-of select="Profile/Name"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Очень трудно перемещаться между разбросанными фрагментами вашего кода. Тем не менее, мне кажется, вы должны изменить свою стратегию на что-то вроде:
<xsl:template match="?">
...
<GalaxyTypes>
<xsl:apply-templates select="??/???/Galaxy">
<xsl:sort select="Profile/Age" data-type="number" order="ascending"/>
</xsl:apply-templates=>
</GalaxyTypes>
...
</xsl:template>
<xsl:template match="Galaxy">
<xsl:choose>
<xsl:when test="Profile/Age=$galaxy_age1">
<GalaxyType1>
<xsl:value-of select="Profile/Name"/>
</GalaxyType1>
</xsl:when>
<xsl:when test="Profile/Age=$galaxy_age2">
<GalaxyType2>
<xsl:value-of select="Profile/Name"/>
</GalaxyType2>
</xsl:when>
<xsl:when test="Profile/Age=$galaxy_age3">
<GalaxyType3>
<xsl:value-of select="Profile/Name"/>
</GalaxyType3>
</xsl:when>
</xsl:choose>
</xsl:template>
-
Обратите внимание, что ваш вывод был бы намного лучше отформатирован, если бы все галактики были одинаковыми <Galaxy>
элемент, с атрибутом типа, чтобы отличать их друг от друга.