(XSLT)如何以递归方式遍历列表中每个项目的相同列表?

时间:2017-05-09 17:23:58

标签: xml xslt-2.0

我是一个相当强大的OOP程序员,所以我在弄清楚XSLT如何“认为”作为一种函数式语言时遇到了一些麻烦。

我正在处理的实际数据很敏感,所以我假设我有一个<albums>的XML列表,其中包含<artist><songs>,每首歌可能会也可能不会有一个<guest_artist>

大概是这样的:

<album>
    <album_name>First One</album_name>
    <artist_name>SomeGuy</artist_name>
    <song>
        <song_name>Somebody</song_name>
        <guest_artist>SomebodyElse</guest_artist>
    </song>
    ...
</album>

我的目标是生成所有<guest_artist>的CSV文本文件,这些文件也是其他<album>的主要艺术家,以及它们作为访客显示的相册。

输出应该如下所示:

Guest Artist Name,Album on which they were a Guest
SombodyElse,First One

我最初的方法是<for-each>超过每个/album/guest_artist。首先,存储来宾艺术家的名字,然后在该循​​环中再次<for-each>在每个../album/artist_name上,并查看存储的变量是否与任何艺术家名称相匹配。在内循环中,如果匹配,我写出一行。

大致相同:

<xsl:variable name="linefeed" select="'&#xA;'"/>
<xsl:template match="/">
    <!-- Header Row Begins -->
    <xsl:textGuest Artist Name,Album on which they were a Guest</xsl:text>
    <xsl:value-of select="$linefeed"/>

    <!-- Data Row Begins -->
    <xsl:for-each select="/album/song/guest_artist">
        <xsl:variable name="guest_name" select="guest_artist"/>
        <xsl:variable name="this_album_name" select="../album_name"/>
        <xsl:for-each select="../../album">
            <xsl:if test="$guest_name=artist_name">
                <xsl:value-of select="album/song/guest_artist"/>
                <xsl:text>,</xsl:text>
                <xsl:value-of select="$this_album_name"/>
                <xsl:value-of select="$linefeed"/>
            </xsl:if>
        </xsl:for-each>
    <!-- Data Row End -->
</xsl:template>

(我并不关心重复。如果我能找出基础知识,那么我可以自己解决这个问题。)

这会产生奇怪的结果。它似乎首先列出所有嘉宾艺术家,然后是逗号,然后是所有专辑名称,然后是换行符。

我不是要求代码(也许是伪代码)。相反,我只是不理解我需要研究的XSLT的功能,以便实现这一点。似乎每个循环都没有像我期望的那样表现。目前还不清楚如何处理范围。我怀疑<templates>会有用,但我很难弄清楚他们做了什么,以及如何做。

我参加过W3学校课程,以及其他一些关于XSL的教程,但它们似乎没有涵盖这些细节。

有什么建议吗?

1 个答案:

答案 0 :(得分:0)

答案结果比我想象的要简单得多。主要问题是每个循环的“上下文”。第一个<for-each>正在评估一个节点,但第二个嵌套<for-each>开始评估另一个节点。因此,我必须能够告诉内部<for-each>外部<for-each>正在评估哪个节点。这就像将其保存在变量中一样简单。 (是的,在其他地方,这被称为“范围”,但我不确定“范围”在这种情况下是正确的术语)。

大致是这样的:

<xsl:variable name="linefeed" select="'&#xA;'"/>
<xsl:template match="/">
    <!-- Header Row Begins -->
    <xsl:textGuest Artist Name,Album on which they were a Guest</xsl:text>
    <xsl:value-of select="$linefeed"/>

    <!-- Data Row Begins -->
    <xsl:for-each select="/album/guest_artist">
--->    <xsl:variable name="current_node" select="current()"/>
        <xsl:variable name="guest_name" select="guest_artist"/>
        <xsl:variable name="this_album_name" select="../album_name"/>
        <xsl:for-each select="../../album">
            <xsl:if test="$guest_name=artist_name">
--->            <xsl:value-of select="$current_node/guest_artist"/>
                <xsl:text>,</xsl:text>
--->            <xsl:value-of select="$current_node/../../album_name"/>
                <xsl:value-of select="$linefeed"/>
            </xsl:if>
        </xsl:for-each>
    <!-- Data Row End -->
</xsl:template>

此示例可能会关闭我的路径。从实际数据到示例场景的转换不是100%,特别是当我还是XSL的新手时。但是,生产版本有效; - )