Переупорядочить узлы, возвращаемые из 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> элемент, с атрибутом типа, чтобы отличать их друг от друга.

Другие вопросы по тегам