Muenchian grouping如何运作?
我有一个从数据库生成的简单XML文档:
<CLIENTS>
<CLIENT>
<NAME>John</NAME>
<ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
<LAST_USED>2012-10-03</LAST_USED>
<AMOUNT>5000</AMOUNT>
</CLIENT>
<CLIENT>
<NAME>John</NAME>
<ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
<LAST_USED>2012-10-02</LAST_USED>
<AMOUNT>10000</AMOUNT>
</CLIENT>
...
我想按名称节点分组。我怎样才能获得以下所需的输出?
<ClIENTS>
<CLIENT>
<NAME>John</NAME>
<ACCOUNT>
<ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
<LAST_USED>2012-10-03</LAST_USED>
<AMOUNT>5000</AMOUNT>
</ACCOUNT>
<ACCOUNT>
<ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
<LAST_USED>2012-10-03</LAST_USED>
<AMOUNT>10000</AMOUNT>
</ACCOUNT>
....
</CLIENTS>
答案 0 :(得分:10)
阅读www.jenitennison.com/xslt/grouping/muenchian.xml,获取代码帮助定义密钥
<xsl:key name="client-by-name" match="CLIENT" use="NAME"/>
然后使用模板
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CLIENTS">
<xsl:copy>
<xsl:apply-templates select="CLIENT[generate-id() = generate-id(key('client-by-name', NAME)[1])]" mode="group"/>
<xsl:copy>
</xsl:template>
<xsl:template match="CLIENT" mode="group">
<xsl:copy>
<xsl:copy-of select="NAME"/>
<xsl:apply-templates select="key('client-by-name', NAME)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CLIENT">
<ACCOUNT>
<xsl:apply-templates select="node()[not(self::NAME)]"/>
</ACCOUNT>
</xsl:template>
[编辑] 如果您想使用XSLT 2.0,那么您当然不需要Muenchian分组,而是使用
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CLIENTS">
<xsl:copy>
<xsl:for-each-group select="CLIENT" group-by="NAME">
<CLIENT>
<xsl:apply-templates select="NAME, current-group()"/>
</CLIENT>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="CLIENT">
<ACCOUNT>
<xsl:apply-templates select="node() except NAME"/>
</ACCOUNT>
</xsl:template>
答案 1 :(得分:4)
Jeni Tennison在这里打破了执行Muenchian分组所需的步骤:
http://www.jenitennison.com/xslt/grouping/muenchian.html
基本上,使用XSLT为节点分配密钥,如果文档中有相同的节点,则可以重复此密钥。然后,XSLT会遍历每个密钥,并允许您输出具有匹配密钥的节点。
所以,在Martin的回答中,这一行是根据NAME节点的内容为每个CLIENT创建一个密钥(请记住,对于多个客户端,NAME是否相同,密钥也是如此):
<xsl:key name="client-by-name" match="CLIENT" use="NAME"/>
然后,您想要查看所有键并找到每个键的第一个实例(再次使用Martin的答案)
<xsl:apply-templates select="CLIENT[generate-id() = generate-id(key('client-by-name', NAME)[1])]" mode="group"/>
然后,您希望找到与密钥匹配的所有CLIENT,以便能够输出其详细信息(同样,Martins)
<xsl:apply-templates select="key('client-by-name', NAME)"/>
从此处您需要另一个模板来输出客户端详细信息
答案 2 :(得分:3)
Muenchian Grouping(根据@ Martin的回答)消除了传统分组策略在分组时的冗余。
如果没有Muenchian分组,模板通常使用preceding-sibling
或following-sibling
来确定每个组的第一个候选实例,然后需要第二个查询来查找与该组匹配的所有节点,如下所示: / p>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="CLIENTS">
<CLIENTS>
<!--Only find the 'first' instance of each client-->
<xsl:apply-templates select="CLIENT[not(NAME = preceding-sibling::CLIENT/NAME)]" mode="client"/>
</CLIENTS>
</xsl:template>
<xsl:template match="CLIENT" mode="client">
<xsl:variable name="name" select="NAME"/>
<CLIENT>
<NAME>
<xsl:value-of select="$name"/>
</NAME>
<ACCOUNTS>
<!--Note that we now have to find the other matching clients *again* - this is the inefficiency that Muenchian grouping eliminates-->
<xsl:apply-templates select="/CLIENTS/CLIENT[NAME/text()=$name]" mode="account" />
</ACCOUNTS>
</CLIENT>
</xsl:template>
<xsl:template match="CLIENT" mode="account">
<ACCOUNT>
<!--Copy everything else except Name, which is the grouping key -->
<xsl:copy-of select="@* | *[not(local-name='NAME')]"/>
</ACCOUNT>
</xsl:template>
</xsl:stylesheet>
答案 3 :(得分:1)
在之前的评论中(在@Martin的回答下),OP询问是否可以使用XSLT 2.0的for-each-group
元素来解决这个问题。
当这个XSLT 2.0解决方案:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/*">
<CLIENTS>
<xsl:for-each-group select="CLIENT" group-by="NAME">
<CLIENT>
<xsl:sequence select="NAME" />
<xsl:for-each select="current-group()">
<ACCOUNT>
<xsl:sequence select="*[not(self::NAME)]" />
</ACCOUNT>
</xsl:for-each>
</CLIENT>
</xsl:for-each-group>
</CLIENTS>
</xsl:template>
</xsl:stylesheet>
...适用于OP的原始XML:
<CLIENTS>
<CLIENT>
<NAME>John</NAME>
<ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
<LAST_USED>2012-10-03</LAST_USED>
<AMOUNT>5000</AMOUNT>
</CLIENT>
<CLIENT>
<NAME>John</NAME>
<ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
<LAST_USED>2012-10-02</LAST_USED>
<AMOUNT>10000</AMOUNT>
</CLIENT>
</CLIENTS>
...生成了想要的结果:
<?xml version="1.0" encoding="utf-8"?>
<CLIENTS>
<CLIENT>
<NAME>John</NAME>
<ACCOUNT>
<ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
<LAST_USED>2012-10-03</LAST_USED>
<AMOUNT>5000</AMOUNT>
</ACCOUNT>
<ACCOUNT>
<ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
<LAST_USED>2012-10-02</LAST_USED>
<AMOUNT>10000</AMOUNT>
</ACCOUNT>
</CLIENT>
</CLIENTS>
<强>解释强>
正如您已经正确推测的那样,XSLT 2.0引入了for-each-group
元素(及其相关合作伙伴,例如current-group()
),以便消除令人惊讶/令人印象深刻但可能令人困惑的分组方法,如Muenchian方法。