Как перебирать многоузловые структуры в XSL - PullRequest
0 голосов
/ 16 марта 2011

Мне нужно перебрать структуры с несколькими узлами, но эта структура идентифицируется только ведущим узлом, и узлы, следующие за этим ведущим узлом, могут различаться.

В этом примере мне нужно перебрать каждый <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 ]

1 голос
/ 16 марта 2011

Во-первых, вам не нужно группировать.Эта таблица стилей:

<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>
1 голос
/ 16 марта 2011

Внутри вашего цикла контекстный узел равен 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>
...