如何在XSL中迭代多节点结构

时间:2011-03-16 01:07:25

标签: xslt xpath xslt-1.0

我需要遍历多节点结构,但此结构仅由前导节点标识,并且该前导节点之后的节点可以变化。

在此示例中,我需要遍历每个<title>-some_nodes<title>-some_other_nodes

XML文件:

<books>
    <title>book_one</title>
    <price>price_for_book_one</price>
    <notes>notes_for_book_one</notes>

    <title>book_two</title>
    <price>price_for_book_two</price>
</books>

期望的输出:

<div class="book">
    <h1 id="title">book_one</h1>
    <h2 id="values">
        <p>price_for_book_one</p>
        <p>notes_for_book_one</p>
    </h2>
</div>

<div class="book">
    <h1 id="title">book_two</h1>
    <h2 id="values">
        <p>price_for_book_two</p>
    </h2>
</div>

我的尝试:

<xsl:template match="/">
    <xsl:for-each select="books/title">
        <h1 id="title"><xsl:text>Title:</xsl:text>
            <xsl:value-of select="." />
        </h1>

        <h2 id="values">    
        <!-- have other templates for matching all possible following-sibling nodes -->
            <xsl:apply-templates select="following-sibling::*>
        </h2>
    </xsl:for-each>
</xsl:template>

但是这只选择标题节点而不解析任何其他节点,因为“books / title”只选择标题节点。

注意:<price><notes>是两个示例节点,<title>节点之间可能有任何内容,我有其他模板可以处理它们,但是如何选择它们是我的问题。

任何想法都会非常感激。

2 个答案:

答案 0 :(得分:1)

在您的循环中,上下文节点为title,因此选择.

您可能只想要标题后面的第一个价格和注释元素。我还使用xsl:text来减少每个段落中的空格。

<xsl:template match="/">
    <xsl:for-each select="books/title">
        <p>
            <xsl:text>Title: </xsl:text>
            <xsl:value-of select="." />
        </p>
        <p>
            <xsl:text>Price: </xsl:text>
            <xsl:value-of select="following-sibling::price[1]"/>
        </p>
        <p>
            <xsl:text>Notes: </xsl:text>
            <xsl:value-of select="following-sibling::notes[1]"/>
        </p>
    </xsl:for-each>
</xsl:template>

如果每个title之间的其他节点没有修复,您可以使用模板处理每个节点。这里重要的是XPath表达式,用于选择属于当前标题的非标题元素。

<xsl:template match="/">
    <xsl:for-each select="books/title">
        <p>
            <xsl:text>Title:</xsl:text>
            <xsl:value-of select="." />
        </p>
        <xsl:apply-templates select="following-sibling::*[
                                       local-name() != 'title' and
                                       preceding-sibling::title[1] = current()]"/>
    </xsl:for-each>
</xsl:template>

<xsl:template match="price">
    <p>
        <xsl:text>Price: </xsl:text>
        <xsl:value-of select="."/>
    </p>
</xsl:template>

答案 1 :(得分:1)

首先,您似乎不需要分组。这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="books/*">
        <p>
            <xsl:value-of
                 select="concat(translate(
                                   substring(local-name(),1,1),
                                   'qwertyuiopasdfghjklzxcvbnm',
                                   'QWERTYUIOPASDFGHJKLZXCVBNM'
                                ),
                               substring(local-name(),2),
                                ': ',
                                .
                         )" />
        </p>
    </xsl:template>
</xsl:stylesheet> 

输出:

<p>Title: book_one</p>
<p>Price: price_for_book_one</p>
<p>Notes: notes_for_book_one</p>
<p>Title: book_two</p>
<p>Price: price_for_book_two</p>

只有当你要对小组做一些事情时才需要分组,例如包裹:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kElementByPrecedingTitle"
             match="books/*"
             use="generate-id((.|preceding-sibling::*)[self::title][last()])"/>
    <xsl:template match="books/*">
        <p>
            <xsl:value-of
                 select="concat(translate(
                                   substring(local-name(),1,1),
                                   'qwertyuiopasdfghjklzxcvbnm',
                                   'QWERTYUIOPASDFGHJKLZXCVBNM'
                                ),
                               substring(local-name(),2),
                                ': ',
                                .
                         )" />
        </p>
    </xsl:template>
    <xsl:template match="books">
        <xsl:for-each select="title">
            <div class="book">
                <xsl:apply-templates
                 select="key('kElementByPrecedingTitle',generate-id())"/>
            </div>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

输出:

<div class="book">
    <p>Title: book_one</p>
    <p>Price: price_for_book_one</p>
    <p>Notes: notes_for_book_one</p>
</div>
<div class="book">
    <p>Title: book_two</p>
    <p>Price: price_for_book_two</p>
</div>