Это не совсем сложно , но ошеломляет из-за широкого (но необходимого) вложенного использования substring-before()
и substring-after()
.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- index <c> nodes by their @id + "uid value" -->
<xsl:key name="kObject" match="r/c" use="
concat(@id, '|', @value)
" />
<!-- index <c> nodes by their @id + "uid value" -->
<xsl:key name="kTagByUid" match="r/c" use="
concat(@id, '|', substring-before(@value, '|'))
" />
<!-- index <c> nodes by their @id + "uid value" + "tag name" -->
<xsl:key name="kTagByName" match="r/c" use="
concat(@id, '|',
substring-before(
@value,
substring-after(substring-after(@value, '|'), '|')
)
)
" />
<xsl:variable name="vTagId" select="/Container/DataHeader/c[@value='TAG'][1]/@id" />
<xsl:variable name="vInfoId" select="/Container/DataHeader/c[@value='Info'][1]/@id" />
<!-- processing starts here -->
<xsl:template match="Container">
<xsl:copy>
<!-- apply templates to unique <c @id=$vTagId> tags -->
<xsl:apply-templates mode="tag" select="
Data/Rows/r/c[@id=$vTagId][
generate-id()
=
generate-id(key('kObject', concat(@id, '|', @value))[1])
]
" />
</xsl:copy>
</xsl:template>
<xsl:template match="c" mode="tag">
<TestTag>
<object UID="{@uid}" name="{@value}" />
<!-- apply templates to unique <c @id="g"> tags -->
<xsl:apply-templates mode="info" select="
key('kTagByUid', concat($vInfoId, '|', @value))[
generate-id()
=
generate-id(
key(
'kTagByName',
concat(@id, '|',
substring-before(
@value,
substring-after(substring-after(@value, '|'), '|')
)
)
)[1]
)
]
" />
</TestTag>
</xsl:template>
<xsl:template match="c" mode="info">
<!-- select 'uid1|tag1|' - it's the key to kTagByName -->
<xsl:variable name="key" select="substring-before(@value, substring-after(substring-after(@value, '|'), '|'))" />
<!-- select 'tag1' - it's the element name -->
<xsl:variable name="name" select="substring-before(substring-after($key, '|'), '|')" />
<xsl:element name="{$name}">
<xsl:for-each select="key('kTagByName', concat(@id, '|', $key))">
<!-- select 'attr1|somevalue1' - it's the attribute definition -->
<xsl:variable name="attrDef" select="substring-after(@value, $key)" />
<!-- create an attribute -->
<xsl:attribute name="{substring-before($attrDef, '|')}">
<xsl:value-of select="substring-after($attrDef, '|')" />
</xsl:attribute>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
генерирует: * +1008 *
<Container>
<TestTag>
<object UID="T.A.uid1" name="uid1" />
<tag1 attr1="somevalue1" attr2="somevalue2"></tag1>
<tag2 attr3="somevalue3"></tag2>
</TestTag>
<TestTag>
<object UID="T.A.uid2" name="uid2" />
<tag1 attr1="somevalue4"></tag1>
<tag2 attr3="somevalue5"></tag2>
</TestTag>
</Container>
Обратите внимание, что при этом не учитываются дубликаты определений атрибутов. Если у вас есть uid1|tag1|attr1|somevalue1
, а затем uid1|tag1|attr1|othervalue1
, то в итоге вы получите один атрибут: attr1="othervalue1"
, потому что в <xsl:for-each>
оба получают свой ход, и последний выигрывает (т.е. попадает на выход) .
Это тоже можно обслужить, для этого потребуется еще один ключ и еще одна мюнхенская группировка, я оставлю это в качестве упражнения для читателя. Хех. ;)