Я брошу свою шляпу в кольцо, даже если вы приняли один ответ ... потому что до сих пор я не думаю, что какое-либо из других решений является правильным (хотя одно из них соответствует заданному примеру вывода).
Вот моя реализация:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" xmlns:p="p">
<xsl:output method="text" />
<xsl:template match="/p:persons">
<xsl:apply-templates select="*[1]">
<xsl:with-param name="i" select="1" />
</xsl:apply-templates>
</xsl:template>
<!-- This template is applied to an element whenever it is
for a new person, i.e. when there is not a preceding
sibling element belonging to the same person. -->
<xsl:template match="p:forename | p:surname">
<xsl:param name="i"/>
<xsl:variable name="next" select="following-sibling::*[1]"/>
<xsl:variable name="surname" select="if (self::p:surname) then . else
if ($next[self::p:surname]) then $next else 'UNKNOWN'"/>
<xsl:variable name="forename" select="if (self::p:forename) then . else
if ($next[self::p:forename]) then $next else 'UNKNOWN'"/>
<xsl:value-of select="concat(if ($i > 1) then ' ' else '',
'--- Person', $i, '--- surname: ',
$surname, ' forename: ', $forename, ' ')"/>
<xsl:choose>
<xsl:when test="local-name($next) = local-name(.)">
<!-- Only one name is supplied. Start processing
the next sibling as a new person. -->
<xsl:apply-templates select="following-sibling::*[1]">
<xsl:with-param name="i" select="$i + 1"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<!-- Both names are supplied. Skip the second name
and start processing the sibling after it.-->
<xsl:apply-templates select="following-sibling::*[2]">
<xsl:with-param name="i" select="$i + 1"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Моя интерпретация неполной спецификации такова: начинайте с первого элемента имени / фамилии. Если первые два элемента - это имя и фамилия (в любом порядке), используйте как для первого лица, так и начните обработку третьего элемента как новый человек.
В противном случае используйте первый элемент и введите «НЕИЗВЕСТНО» для имени, которое не указано. Затем начните обработку следующего элемента как нового человека.
С этой спецификационной интерпретацией, я почти уверен, что невозможно правильно реализовать без какой-либо рекурсии, потому что ассоциация каждого элемента с предшествующим или последующим может зависеть от того, как предыдущий связан (например, имя первым?), которые могут зависеть от человека до этого и т. д.
Первый приведенный выше пример дает желаемый результат.
Второй (с исправленными тегами) дает:
--- Person1---
surname: Surname1
forename: Forename1
--- Person2---
surname: UNKNOWN
forename: Forename2
Более полный пример:
<p:persons xmlns:p="p">
<p:surname>Surname1</p:surname>
<p:forename>Forename1</p:forename>
<p:surname>Surname2</p:surname>
<p:surname>Surname3</p:surname>
<p:surname>Surname4</p:surname>
<p:forename>Forename4</p:forename>
<p:forename>Forename5</p:forename>
<p:surname>Surname5</p:surname>
</p:persons>
дает
--- Person1---
surname: Surname1
forename: Forename1
--- Person2---
surname: Surname2
forename: UNKNOWN
--- Person3---
surname: Surname3
forename: UNKNOWN
--- Person4---
surname: Surname4
forename: Forename4
--- Person5---
surname: Surname5
forename: Forename5