XSLT: выберите «следующий брат» до достижения указанного тега - PullRequest
18 голосов
/ 30 января 2010

Я пытаюсь написать XSLT, который будет запускать for-each для выбранных потомков, но останавливаться при достижении другого тега (h1).

Вот исходный XML:

<?xml version="1.0"?>
<html>
    <h1>Test</h1>
    <p>Test: p 1</p>
    <p>Test: p 2</p>
    <h1>Test 2</h1>
    <p>Test2: p 1</p>
    <p>Test2: p 2</p>
    <p>Test2: p 3</p>
</html>

Вот XSLT:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">
        <content>
            <xsl:apply-templates/>
        </content>
    </xsl:template>

    <xsl:template match="h1">
        <section>
            <sectionHeading>
                <xsl:apply-templates/>
            </sectionHeading>
            <sectionContent>
                <xsl:for-each select="following-sibling::p">
                    <paragraph>
                        <xsl:value-of select="."/>
                    </paragraph>
                </xsl:for-each>
            </sectionContent>
        </section>
    </xsl:template>

    <xsl:template match="p"/>
</xsl:stylesheet>

Вот текущий результат:

<?xml version="1.0" encoding="UTF-8"?>
<content>
    <section>
        <sectionHeading>Test</sectionHeading>
        <sectionContent>
            <paragraph>Test: p 1</paragraph>
            <paragraph>Test: p 2</paragraph>
            <paragraph>Test: p 3</paragraph>
            <paragraph>Test2: p 1</paragraph>
            <paragraph>Test2: p 2</paragraph>
        </sectionContent>
    </section>
    <section>
        <sectionHeading>Test 2</sectionHeading>
        <sectionContent>
            <paragraph>Test2: p 1</paragraph>
            <paragraph>Test2: p 2</paragraph>
        </sectionContent>
    </section>
</content>

Вот ожидаемый результат:

<?xml version="1.0" encoding="UTF-8"?>
<content>
<section>
    <sectionHeading>Test</sectionHeading>
    <sectionContent>
        <paragraph>Test: p 1</paragraph>
        <paragraph>Test: p 2</paragraph>
        <paragraph>Test: p 3</paragraph>
    </sectionContent>
</section>
<section>
    <sectionHeading>Test 2</sectionHeading>
    <sectionContent>
        <paragraph>Test2: p 1</paragraph>
        <paragraph>Test2: p 2</paragraph>
    </sectionContent>
</section>
</content>

Ответы [ 2 ]

25 голосов
/ 30 января 2010

Попробуйте это: (Вместо того, чтобы запрашивать все p, мы просим все p, последний из которых предшествует h1 - текущий).

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">
        <content>
            <xsl:apply-templates/>
        </content>
    </xsl:template>

    <xsl:template match="h1">
        <xsl:variable name="header" select="."/>
        <section>
            <sectionHeading>
                <xsl:apply-templates/>
            </sectionHeading>
            <sectionContent>
                <xsl:for-each select="following-sibling::p[preceding-sibling::h1[1] = $header]">
                    <paragraph>
                        <xsl:value-of select="."/>
                    </paragraph>
                </xsl:for-each>
            </sectionContent>
        </section>
    </xsl:template>

    <xsl:template match="p"/>
</xsl:stylesheet>
9 голосов
/ 23 ноября 2016

Принятый ответ имеет плохой побочный эффект, и это своего рода неправильно.

Далее в этом посте я объясню реальное сравнение следующего существенного утверждения и почему оно может и потерпит неудачу .


пересмотреть / проанализировать ситуацию, находясь в шаблоне <xsl:template match="h1">:

  • Текущий контекстный узел - это любой h1 из соответствующего <xsl:template>.
  • переменная с именем header содержит копию моего текущего узла контекста.

Существенное утверждение, которое является плохим / неправильным:

follow-sibling :: p [previousing-sibling :: h1 [1] = $ header]

  • выбрать всех следующих братьев и сестер p моего контекстного узла | following-sibling::p
  • отфильтруйте эти p, где первый (ближайший) предшествующий брат с именем h1 "равен" так же, как переменная $header | ...[preceding-sibling::h1[1] = $header].

!! В XSLT 1.0 сравнение узла с узлом будет выполняться по его значению !!


Посмотрите это на примере. Давайте представим, что входной xml выглядит следующим образом [<h1> содержит дважды одинаковое значение Test]:

<html>
    <h1>Test</h1>
    <p>Test: p 1</p>
    <p>Test: p 2</p>
    <h1>Test</h1>
    <p>Test2: p 1</p>
    <p>Test2: p 2</p>
    <p>Test2: p 3</p>
</html>

A ! WRONG! будет создан результат:

<content>
  <section>
     <sectionHeading>Test</sectionHeading>
     <sectionContent>
        <paragraph>Test: p 1</paragraph>
        <paragraph>Test: p 2</paragraph>
        <paragraph>Test2: p 1</paragraph> <-- should be only in 2. section 
        <paragraph>Test2: p 2</paragraph> <-- should be only in 2. section 
        <paragraph>Test2: p 3</paragraph> <-- should be only in 2. section 
     </sectionContent>
  </section>
  <section>
     <sectionHeading>Test</sectionHeading>
     <sectionContent>
        <paragraph>Test2: p 1</paragraph>
        <paragraph>Test2: p 2</paragraph>
        <paragraph>Test2: p 3</paragraph>
     </sectionContent>
  </section>
</content>

Правильное сравнение

...
<xsl:template match="h1">
    <xsl:variable name="header" select="generate-id(.)"/>
    <section>
        <sectionHeading>
            <xsl:apply-templates/>
        </sectionHeading>
        <sectionContent>
            <xsl:for-each select="following-sibling::p[generate-id(preceding-sibling::h1[1]) = $header]">
                <paragraph>
                    <xsl:value-of select="."/>
                </paragraph>
            </xsl:for-each>
        </sectionContent>
    </section>
</xsl:template>
...

Используйте функцию generate-id () , чтобы получить уникальный (по крайней мере, в текущем документе) идентификатор узла и сравнить теперь узел с узлом! Даже если вы используете эту технику с <xsl:key>, вы должны использовать generate-id().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...