xslt gmuenchian 分组与子组
xslt gmuenchian grouping with sub-groups
我正在尝试将 xml 数据重组为组和子组。我能够让它工作,但我的代码必须包含一些东西,看起来(至少对我来说)像一个解决方法。这是我的示例文件:
Data.xml:
<data>
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g2" SubGroup="sg1">Record 2</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
<record Group="g2" SubGroup="sg1">Record 4</record>
<record Group="g2" SubGroup="sg2">Record 5</record>
<record Group="g1" SubGroup="sg2">Record 6</record>
</data>
Stylesheet.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/>
<xsl:key name="Group" match="record" use="@Group" />
<xsl:key name="SubGroup" match="record" use="@SubGroup" />
<xsl:template match="/data">
<xsl:variable name="Records" select="record"/>
<data>
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('Group',@Group)[1])]">
<xsl:sort select="@Group"/>
<xsl:variable name="Group" select="@Group"/>
<xsl:call-template name="Group">
<xsl:with-param name="Records" select="$Records[@Group = $Group]"/>
<xsl:with-param name="Group" select="$Group"/>
</xsl:call-template>
</xsl:for-each>
</data>
</xsl:template>
<xsl:template name="Group">
<xsl:param name="Records"/>
<xsl:param name="Group"/>
<group name="{$Group}">
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',@SubGroup)[1])]">
<!-- this works: <xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',@SubGroup)[@Group = $Group][1])]"> -->
<xsl:sort select="@SubGroup"/>
<xsl:variable name="SubGroup" select="@SubGroup"/>
<xsl:call-template name="SubGroup">
<xsl:with-param name="Records" select="$Records[@SubGroup = $SubGroup]"/>
<xsl:with-param name="Group" select="$Group"/>
<xsl:with-param name="SubGroup" select="$SubGroup"/>
</xsl:call-template>
</xsl:for-each>
</group>
</xsl:template>
<xsl:template name="SubGroup">
<xsl:param name="Records"/>
<xsl:param name="Group"/>
<xsl:param name="SubGroup"/>
<subgroup name="{$SubGroup}">
<xsl:for-each select="$Records">
<xsl:copy-of select="."/>
</xsl:for-each>
</subgroup>
</xsl:template>
</xsl:stylesheet>
这是生成的输出:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<group name="g1">
<subgroup name="sg1">
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
</subgroup>
</group>
<group name="g2">
<subgroup name="sg2">
<record Group="g2" SubGroup="sg2">Record 5</record>
</subgroup>
</group>
</data>
但这是输出,我想要:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<group name="g1">
<subgroup name="sg1">
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
</subgroup>
<subgroup name="sg2">
<record Group="g1" SubGroup="sg2">Record 6</record>
</subgroup>
</group>
<group name="g2">
<subgroup name="sg1">
<record Group="g2" SubGroup="sg1">Record 2</record>
<record Group="g2" SubGroup="sg1">Record 4</record>
</subgroup>
<subgroup name="sg2">
<record Group="g2" SubGroup="sg2">Record 5</record>
</subgroup>
</group>
</data>
问题出在名为 "Group" 的模板中的 for-each 循环。看起来,key() 函数不适用于 $Records 中包含的节点,而是整个输入 XML 文件。
我用 xsltproc 和 saxon 得到了相同的结果,所以我不认为这是我的 xslt 处理器中的错误。看来,我没有完全理解 key() 是如何工作的。
如果我在 key() 的输出中添加一个额外的选择器 [@Group = $Group]
,我会得到预期的结果。
有人可以解释发生了什么以及为什么需要额外的选择器 [@Group = $Group]
。
马里奥
当你想做子分组时,你需要使用主分组和子分组的连接键
<xsl:key name="SubGroup" match="record" use="concat(@Group,'|', @SubGroup)" />
然后,像以前一样使用它,连接起来
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',concat(@Group,'|', @SubGroup))[1])]">
试试这个 XSLT(我也简化了在调用带有记录的命名模板时使用密钥)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/>
<xsl:key name="Group" match="record" use="@Group" />
<xsl:key name="SubGroup" match="record" use="concat(@Group,'|', @SubGroup)" />
<xsl:template match="/data">
<data>
<xsl:for-each select="record[generate-id(.)=generate-id(key('Group',@Group)[1])]">
<xsl:sort select="@Group"/>
<xsl:call-template name="Group">
<xsl:with-param name="Records" select="key('Group',@Group)"/>
</xsl:call-template>
</xsl:for-each>
</data>
</xsl:template>
<xsl:template name="Group">
<xsl:param name="Records"/>
<group name="{@Group}">
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',concat(@Group,'|', @SubGroup))[1])]">
<xsl:sort select="@SubGroup"/>
<xsl:call-template name="SubGroup">
<xsl:with-param name="Records" select="key('SubGroup',concat(@Group,'|', @SubGroup))"/>
</xsl:call-template>
</xsl:for-each>
</group>
</xsl:template>
<xsl:template name="SubGroup">
<xsl:param name="Records"/>
<subgroup name="{@SubGroup}">
<xsl:for-each select="$Records">
<xsl:copy-of select="."/>
</xsl:for-each>
</subgroup>
</xsl:template>
</xsl:stylesheet>
我正在尝试将 xml 数据重组为组和子组。我能够让它工作,但我的代码必须包含一些东西,看起来(至少对我来说)像一个解决方法。这是我的示例文件:
Data.xml:
<data>
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g2" SubGroup="sg1">Record 2</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
<record Group="g2" SubGroup="sg1">Record 4</record>
<record Group="g2" SubGroup="sg2">Record 5</record>
<record Group="g1" SubGroup="sg2">Record 6</record>
</data>
Stylesheet.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/>
<xsl:key name="Group" match="record" use="@Group" />
<xsl:key name="SubGroup" match="record" use="@SubGroup" />
<xsl:template match="/data">
<xsl:variable name="Records" select="record"/>
<data>
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('Group',@Group)[1])]">
<xsl:sort select="@Group"/>
<xsl:variable name="Group" select="@Group"/>
<xsl:call-template name="Group">
<xsl:with-param name="Records" select="$Records[@Group = $Group]"/>
<xsl:with-param name="Group" select="$Group"/>
</xsl:call-template>
</xsl:for-each>
</data>
</xsl:template>
<xsl:template name="Group">
<xsl:param name="Records"/>
<xsl:param name="Group"/>
<group name="{$Group}">
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',@SubGroup)[1])]">
<!-- this works: <xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',@SubGroup)[@Group = $Group][1])]"> -->
<xsl:sort select="@SubGroup"/>
<xsl:variable name="SubGroup" select="@SubGroup"/>
<xsl:call-template name="SubGroup">
<xsl:with-param name="Records" select="$Records[@SubGroup = $SubGroup]"/>
<xsl:with-param name="Group" select="$Group"/>
<xsl:with-param name="SubGroup" select="$SubGroup"/>
</xsl:call-template>
</xsl:for-each>
</group>
</xsl:template>
<xsl:template name="SubGroup">
<xsl:param name="Records"/>
<xsl:param name="Group"/>
<xsl:param name="SubGroup"/>
<subgroup name="{$SubGroup}">
<xsl:for-each select="$Records">
<xsl:copy-of select="."/>
</xsl:for-each>
</subgroup>
</xsl:template>
</xsl:stylesheet>
这是生成的输出:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<group name="g1">
<subgroup name="sg1">
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
</subgroup>
</group>
<group name="g2">
<subgroup name="sg2">
<record Group="g2" SubGroup="sg2">Record 5</record>
</subgroup>
</group>
</data>
但这是输出,我想要:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<group name="g1">
<subgroup name="sg1">
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
</subgroup>
<subgroup name="sg2">
<record Group="g1" SubGroup="sg2">Record 6</record>
</subgroup>
</group>
<group name="g2">
<subgroup name="sg1">
<record Group="g2" SubGroup="sg1">Record 2</record>
<record Group="g2" SubGroup="sg1">Record 4</record>
</subgroup>
<subgroup name="sg2">
<record Group="g2" SubGroup="sg2">Record 5</record>
</subgroup>
</group>
</data>
问题出在名为 "Group" 的模板中的 for-each 循环。看起来,key() 函数不适用于 $Records 中包含的节点,而是整个输入 XML 文件。
我用 xsltproc 和 saxon 得到了相同的结果,所以我不认为这是我的 xslt 处理器中的错误。看来,我没有完全理解 key() 是如何工作的。
如果我在 key() 的输出中添加一个额外的选择器 [@Group = $Group]
,我会得到预期的结果。
有人可以解释发生了什么以及为什么需要额外的选择器 [@Group = $Group]
。
马里奥
当你想做子分组时,你需要使用主分组和子分组的连接键
<xsl:key name="SubGroup" match="record" use="concat(@Group,'|', @SubGroup)" />
然后,像以前一样使用它,连接起来
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',concat(@Group,'|', @SubGroup))[1])]">
试试这个 XSLT(我也简化了在调用带有记录的命名模板时使用密钥)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/>
<xsl:key name="Group" match="record" use="@Group" />
<xsl:key name="SubGroup" match="record" use="concat(@Group,'|', @SubGroup)" />
<xsl:template match="/data">
<data>
<xsl:for-each select="record[generate-id(.)=generate-id(key('Group',@Group)[1])]">
<xsl:sort select="@Group"/>
<xsl:call-template name="Group">
<xsl:with-param name="Records" select="key('Group',@Group)"/>
</xsl:call-template>
</xsl:for-each>
</data>
</xsl:template>
<xsl:template name="Group">
<xsl:param name="Records"/>
<group name="{@Group}">
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',concat(@Group,'|', @SubGroup))[1])]">
<xsl:sort select="@SubGroup"/>
<xsl:call-template name="SubGroup">
<xsl:with-param name="Records" select="key('SubGroup',concat(@Group,'|', @SubGroup))"/>
</xsl:call-template>
</xsl:for-each>
</group>
</xsl:template>
<xsl:template name="SubGroup">
<xsl:param name="Records"/>
<subgroup name="{@SubGroup}">
<xsl:for-each select="$Records">
<xsl:copy-of select="."/>
</xsl:for-each>
</subgroup>
</xsl:template>
</xsl:stylesheet>