Я думаю, что в XQuery 3 вы можете решить эту проблему, используя два вложенных выражения for .. group by
:
/*/element { node-name(.) } {
for $child-element at $pos in *
group by $element-name := node-name($child-element)
order by $pos[1]
return
element { $element-name } {
for $grand-child at $pos in $child-element/*
let $grand-child-name := node-name($grand-child)
group by $key := $grand-child-name, $handle := contains(string($grand-child-name), '_list')
order by $pos[1]
return
if ($handle)
then
element { $key } {
$grand-child/*
}
else $grand-child
}
}
https://xqueryfiddle.liberty -development.net / pPgCcor
Для XSLT 1 я хотел бы использовать ключи, как уже предложенное решение, но я думаю, что тогда проще использовать два разных шаблона соответствия для каждого ключа, по одному для первого элемента в группе, созданной ключом, который создает копию и обрабатывает дочерние узлыгруппа, а вторая пуста, чтобы подавить обработку дублированных имен элементов группы:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="child-group" match="/*/*" use="name()"/>
<xsl:key name="grand-child-group" match="/*/*/*[contains(local-name(), '_list')]" use="name()"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/*[generate-id() = generate-id(key('child-group', name())[1])]">
<xsl:copy>
<xsl:apply-templates select="key('child-group', name())/node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/*[not(generate-id() = generate-id(key('child-group', name())[1]))]"/>
<xsl:template match="/*/*/*[contains(local-name(), '_list')][generate-id() = generate-id(key('grand-child-group', name())[1])]">
<xsl:copy>
<xsl:apply-templates select="key('grand-child-group', name())/node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/*/*[contains(local-name(), '_list')][not(generate-id() = generate-id(key('grand-child-group', name())[1]))]"/>
</xsl:stylesheet>
https://xsltfiddle.liberty -development.net / jyH9rN5
На основеВ вашем комментарии я также попытался сделать рекурсивное решение XQuery 3:
declare function local:group($elements as element()*) as element()*
{
for $child-element at $pos in $elements
let $child-name := node-name($child-element)
group by $name-group := $child-name, $match := contains(string($child-name), '_list')
order by $pos[1]
return
if ($match)
then element { $name-group } {
local:group($child-element/*)
}
else if (not($child-element/*))
then $child-element
else $child-element/element {$name-group} { local:group(*) }
};
/*/element { node-name(.) } {
for $child-element at $pos in *
group by $element-name := node-name($child-element)
order by $pos[1]
return element { $element-name } {
local:group($child-element/*)
}
}
https://xqueryfiddle.liberty -development.net / pPgCcor / 1