Мне нравится использовать функциональные аспекты XSL, которые привели меня к следующей реализации:
<?xml version="1.0" encoding="UTF-8"?>
<!-- test data inlined -->
<test>
<one>Content 1</one>
<two>Content 2</two>
<three>Content 3</three>
<four/>
<special>I'm special!</special>
</test>
<!-- any root since take test content from stylesheet -->
<xsl:template match="/">
<html>
<head>
<title>Header/Content Widget</title>
</head>
<body>
<xsl:apply-templates select="document('')//test/*" mode="header-content-widget"/>
</body>
</html>
</xsl:template>
<!-- default action for header-content -widget is apply header then content views -->
<xsl:template match="*" mode="header-content-widget">
<xsl:apply-templates select="." mode="header-view"/>
<xsl:apply-templates select="." mode="content-view"/>
</xsl:template>
<!-- default header-view places element name in <h2> tag -->
<xsl:template match="*" mode="header-view">
<h2><xsl:value-of select="name()"/></h2>
</xsl:template>
<!-- default header-view when no text content is no-op -->
<xsl:template match="*[not(text())]" mode="header-view"/>
<!-- default content-view is to apply-templates -->
<xsl:template match="*" mode="content-view">
<xsl:apply-templates/>
</xsl:template>
<!-- special content handling -->
<xsl:template match="special" mode="content-view">
<strong><xsl:apply-templates/></strong>
</xsl:template>
Однажды в теле все элементы, содержащиеся в элементе test , имеют header-content-widget (в порядке документа).
Шаблон по умолчанию header-content-widget (соответствует "*") сначала применяет header-view , затем применяет content-view к текущему элемент.
Стандартный шаблон header-view помещает имя текущего элемента в тег h2. По умолчанию представление содержимого применяет общие правила обработки.
Когда нет содержимого, как судят по предикату [not (text ())] , выход для элемента не происходит.
Один специальный чехол легко обрабатывается.