XSLT:递归合并具有相同名称的节点
XSLT: merge nodes with same name recursively
虽然在 SO 上有很多类似标题的问题,但我找不到我的具体问题的答案。
假设我有一棵 xml
树:
<input>
<a>
<b>
<p1/>
</b>
</a>
<a>
<b>
<p2/>
</b>
</a>
</input>
我想要这样
<input>
<a>
<b>
<p1/>
<p2/>
</b>
</a>
</input>
这个转换背后的想法是将一棵树,其中一个节点可以有多个 children 同名到更多 'well-formed' 树,其中每个节点只能有一个 child同名。 (c.f。文件系统)。
我尝试使用 xslt-2
的 grouping-feature,但无法使递归工作。
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<output>
<xsl:apply-templates select="*"/>
</output>
</xsl:template>
<!-- this merges the children -->
<xsl:template name="merge" match="*">
<xsl:for-each-group select="child::*" group-by="local-name()">
<xsl:variable name="x" select="current-grouping-key()"/>
<xsl:element name="{$x}">
<xsl:copy-of select="current-group()/@*"/>
<xsl:apply-templates select="current-group()"/>
<xsl:copy-of select="text()"/>
</xsl:element>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
我看到问题是我正在为 current-group()
中的每个节点单独应用模板,但我不知道如何才能先 "join" 这个集合并应用模板整体
我认为你可以设置一个带有分组的函数,参见 http://xsltransform.net/bdxtqM/1,
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="mf">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:function name="mf:merge" as="node()*">
<xsl:param name="elements" as="element()*"/>
<xsl:for-each-group select="$elements" group-by="node-name(.)">
<xsl:copy>
<xsl:copy-of select="current-group()/@*"/>
<xsl:sequence select="mf:merge(current-group()/*)"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="/*">
<xsl:copy>
<xsl:sequence select="mf:merge(*)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
虽然在 SO 上有很多类似标题的问题,但我找不到我的具体问题的答案。
假设我有一棵 xml
树:
<input>
<a>
<b>
<p1/>
</b>
</a>
<a>
<b>
<p2/>
</b>
</a>
</input>
我想要这样
<input>
<a>
<b>
<p1/>
<p2/>
</b>
</a>
</input>
这个转换背后的想法是将一棵树,其中一个节点可以有多个 children 同名到更多 'well-formed' 树,其中每个节点只能有一个 child同名。 (c.f。文件系统)。
我尝试使用 xslt-2
的 grouping-feature,但无法使递归工作。
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<output>
<xsl:apply-templates select="*"/>
</output>
</xsl:template>
<!-- this merges the children -->
<xsl:template name="merge" match="*">
<xsl:for-each-group select="child::*" group-by="local-name()">
<xsl:variable name="x" select="current-grouping-key()"/>
<xsl:element name="{$x}">
<xsl:copy-of select="current-group()/@*"/>
<xsl:apply-templates select="current-group()"/>
<xsl:copy-of select="text()"/>
</xsl:element>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
我看到问题是我正在为 current-group()
中的每个节点单独应用模板,但我不知道如何才能先 "join" 这个集合并应用模板整体
我认为你可以设置一个带有分组的函数,参见 http://xsltransform.net/bdxtqM/1,
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="mf">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:function name="mf:merge" as="node()*">
<xsl:param name="elements" as="element()*"/>
<xsl:for-each-group select="$elements" group-by="node-name(.)">
<xsl:copy>
<xsl:copy-of select="current-group()/@*"/>
<xsl:sequence select="mf:merge(current-group()/*)"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="/*">
<xsl:copy>
<xsl:sequence select="mf:merge(*)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>