XSLT: выберите следующий сиблинг, пока не достигнете указанного тега
Я пытаюсь написать 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>
Ответы
Ответ 1
Попробуйте следующее: (Вместо того, чтобы просить все 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>
Ответ 2
Принятый ответ имеет плохой побочный эффект, и это своего рода неправильно.
Далее в этом посте я объясню реальное сравнение следующего существенного утверждения и почему оно может и не удастся.
пересмотреть/проанализировать ситуацию, находясь в шаблоне <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 ! НЕПРАВИЛЬНО! Результат будет создан:
<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()
.