Это преобразование :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my" exclude-result-prefixes="my xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vSegments" select=
"tokenize(translate(/*, ' 	

', ''),
'~')
[.]
"/>
<xsl:template match="/*">
<xsl:sequence select="my:buildXml($vSegments)"/>
</xsl:template>
<xsl:function name="my:buildXml">
<xsl:param name="pSegments" as="xs:string*"/>
<xsl:element name="{$pSegments[1]}">
<xsl:sequence select="my:buildXml2($pSegments[position() >1])"/>
</xsl:element>
</xsl:function>
<xsl:function name="my:buildXml2">
<xsl:param name="pSegments" as="xs:string*"/>
<xsl:for-each-group select="$pSegments"
group-adjacent="substring-before(concat(.,'/'),'/')">
<xsl:element name="{current-grouping-key()}">
<xsl:variable name="vsubSegments" select=
"for $subSeg in current-group(),
$subSeqTail in substring-after($subSeg, '/')
return
$subSeqTail[.]
"/>
<xsl:sequence select=
"my:buildXml2($vsubSegments)"/>
</xsl:element>
</xsl:for-each-group>
</xsl:function>
</xsl:stylesheet>
при применении к следующему XML-документу (используя немного более сложную строку, чтобы сделать это еще более сложным):
<t>
author
~time
~assignedAuthor
~assignedAuthor/id
~assignedAuthor/addr
~assignedAuthor/telecom
~assignedAuthor/assignedPerson/name
~assignedAuthor/assignedPerson/address~
</t>
дает желаемый, правильный результат :
<author>
<time/>
<assignedAuthor>
<id/>
<addr/>
<telecom/>
<assignedPerson>
<name/>
<address/>
</assignedPerson>
</assignedAuthor>
</author>
Объяснение :
Токенизация последовательности «сегментов». Использование tokenize()
.
Группировка (xsl:for-each-group
) с атрибутом group-adjacent
с использованием в качестве ключа группировки первого «подсегмента».
Для каждой группы, которая рекурсивно строит поддерево XML. Использование current-grouping-key()
и current-group()