高级 muenchian 分组:按子集合中的项目分组

Advanced muenchian grouping: group by items in child collection

我熟悉 XSL 中的简单 muenchian 分组,但我遇到了一个问题,老实说我什至不知道如何处理它。

所以我有一个 XML:

<whiskies>
    <whisky name="ABC" percent="" region="" type="">
        <tastingNotesCollection/>
        <bottles>
            <bottle price="" size="" level="0" date=""/>
            <bottle price="" size="" level="70" date=""/>
            <bottle price="" size="" level="100" date=""/>
        </bottles>
        <comments/>
    </whisky>
    <whisky name="DEF" percent="" region="" type="">
        <tastingNotesCollection/>
        <bottles>
            <bottle price="" size="" level="0" date=""/>
            <bottle price="" size="" level="100" date=""/>
        </bottles>
        <comments/>
    </whisky>
    <whisky name="GHI" percent="" region="" type="">
        <tastingNotesCollection/>
        <bottles>
            <bottle price="" size="" level="30" date=""/>
        </bottles>
        <comments/>
    </whisky>
<whiskies>

目标是按瓶中的级别对威士忌进行分组: 所以 level="0" 被认为是空的。 从 level="1"level="99" 的任何内容都被认为是开放的。 level="100" 视为未开封。

因此转换后的结果(将在 HTML 中完成)应该如下所示:

<h1>Empty</h1>
<ul>
    <li>ABC</li>
    <li>DEF</li>
</ul>
<h1>Open</h1>
<ul>
    <li>ABC</li>
    <li>GHI</li>
</ul>
<h1>Unopened</h1>
<ul>
    <li>ABC</li>
    <li>DEF</li>
</ul>

如您所见,同一种威士忌可以出现在多个组中,具体取决于瓶子的数量以及这些瓶子的装满程度。 第二个问题是,“开放”组没有确切的值,可以是 1 到 99 之间的任意值。

所以,是的,我真的不知道这是否可以解决,甚至不知道如何开始。任何提示表示赞赏。

我认为您不想为此使用 Muenchian 分组。您需要定义一个键来枚举 1 到 99 范围内的所有值 - 我相信这会带走使用键本来可以带来的任何优势。

由于您的结果只有 3 组或最多 3 组(您的问题在这方面不明确),您可以简单地做:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:template match="/whiskies">
    <output>
        <group status="empty">
            <xsl:for-each select="whisky[bottles/bottle/@level=0]">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
        <group status="open">
            <xsl:for-each select="whisky[bottles/bottle[@level>0 and @level &lt; 100]]">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
        <group status="unopened">
            <xsl:for-each select="whisky[bottles/bottle/@level=100]">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
    </output>
</xsl:template>

</xsl:stylesheet>

得到(将输入固定为格式正确的XML!):

结果

<?xml version="1.0" encoding="utf-8"?>
<output>
  <group status="empty">
    <name>ABC</name>
    <name>DEF</name>
  </group>
  <group status="open">
    <name>ABC</name>
    <name>GHI</name>
  </group>
  <group status="unopened">
    <name>ABC</name>
    <name>DEF</name>
  </group>
</output>

对 HTML 输出进行自己的调整。


已添加:

这是一种使用键的替代方法(尽管仍然不是 Muenchian 分组),它的性能可能更高:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="w" match="whisky" use="bottles/bottle/@level" />
<xsl:key name="w1" match="whisky" use="boolean(bottles/bottle[@level!=0 and @level!=100])" />

<xsl:template match="/whiskies">
    <output>
        <group status="empty">
            <xsl:for-each select="key('w', 0)">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
        <group status="open">
            <xsl:for-each select="key('w1', true())">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
        <group status="unopened">
            <xsl:for-each select="key('w', 100)">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
    </output>
</xsl:template>

</xsl:stylesheet>

甚至:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="bottle" match="bottle" use="ceiling(@level div 99)" />

<xsl:template match="/whiskies">
    <output>
        <group status="empty">
            <xsl:for-each select="key('bottle', 0)/../..">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
        <group status="open">
            <xsl:for-each select="key('bottle', 1)/../..">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
        <group status="unopened">
            <xsl:for-each select="key('bottle', 2)/../..">
                <name>
                    <xsl:value-of select="@name"/>
                </name>
            </xsl:for-each>
        </group>
    </output>
</xsl:template>

</xsl:stylesheet>

如果您确实想使用 xsl:key,您可以使用过滤条件创建其中的 3 个:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" />
    
    <xsl:key name="Empty" match="whisky[bottles/bottle/@level=0]" use="@name"/>
    <xsl:key name="Open" match="whisky[bottles/bottle/@level[. > 1 and . &lt; 99]]" use="@name"/>
    <xsl:key name="Unopened" match="whisky[bottles/bottle/@level=100]" use="@name"/>
        
    <xsl:template match="/">
        <xsl:variable name="whiskies" select="/whiskies/whisky/@name"/>
        <xsl:for-each select="document('')/xsl:stylesheet/xsl:key/@name">
            <xsl:variable name="status" select="."/>
            <h1><xsl:value-of select="$status"/></h1>
            <ul>
                <xsl:for-each select="$whiskies[key($status, .)]">
                    <li><xsl:value-of select="."/></li>
                </xsl:for-each>
            </ul>
        </xsl:for-each>     
    </xsl:template>

</xsl:stylesheet>