重新排序从xsl:choose返回的节点

时间:2014-07-17 20:31:04

标签: xslt xslt-1.0 remap xsl-choose

对使用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 个答案:

答案 0 :(得分:1)

我猜你想要首先匹配 galaxy-age1 的星系,接下来匹配 galaxy-age2 的星系,最后是 >星系age3 即可。我也假设指定的年龄可能不是按升序排列的(也就是说, galaxy-age3 可能小于 galaxy-age1

首先,在 Galaxy 元素上执行 xsl:for-each 可能更为自然

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

答案 1 :(得分:0)

在代码的分散代码段之间导航非常困难。不过,在我看来,你应该把你的策略改为:

<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>元素,并且类型为属性,则可以更好地格式化您的输出。