Xml 在XSLT密钥生成中使用模板

Xml 在XSLT密钥生成中使用模板,xml,xslt,xslt-1.0,Xml,Xslt,Xslt 1.0,我有一个包含项目的XML文档,每个项目都有一个标记化的ID字符串 <?xml version="1.0"?> <Test> <Items> <Item> <ID>1_A_3</ID> <Name>foo</Name> </Item> <Item> <ID>1_B_5</ID>

我有一个包含项目的XML文档,每个项目都有一个标记化的ID字符串

<?xml version="1.0"?>
<Test>
<Items>
    <Item>
        <ID>1_A_3</ID>
        <Name>foo</Name>
    </Item>
    <Item>
        <ID>1_B_5</ID>
        <Name>bar</Name>
    </Item>
    <Item>
        <ID>1_B_7</ID>
        <Name>baz</Name>
    </Item>
</Items>
</Test>

1_A_3
福
1_B_5
酒吧
1_B_7
巴兹
我需要将其转换为另一个XML文档,该文档根据项目ID字符串的中间部分(上面示例中的字母)对项目进行分组


福
酒吧
巴兹
我正在使用密钥功能查找组:

<xsl:key name="uniqueGroupIDs" 
   match="Test/Items/Item" 
   use="substring-before(substring-after(ID,'_'),'_')"/>

<xsl:for-each 
   select="Test/Items/Item[generate-id() = 
    generate-id(key('uniqueGroupIDs',
    substring-before(substring-after(ID,'_'),'_')))]">

请注意,这两个位置的子字符串调用都有代码重复。我已经有了一个做同样事情的模板:

<xsl:template name="ExtractGroupID">
    <xsl:param name="idString"/>        
<xsl:value-of 
       select="substring-before(substring-after($idString, '_'),'_')"/>
</xsl:template>

有没有办法在关键语句中使用该模板,以避免代码重复


在XSLT 2.0中,我只想定义一个函数来实现这一点,但由于我无法控制的工具限制,我一直坚持使用XSLT 1.0。

如果要使用Muenchian分组方法,那么在vanilla XSLT 1.0中,您将不得不重复代码。能够在密钥提取中使用模板的解决方案速度会慢得多,可读性也会差得多。

首先,如果您使用Muenchian分组方法查看这里的所有答案,您将看到它们中的每一个都“复制”了密钥值计算

唯一的“解决方案”是两阶段转换:一个模板只向输入源添加一个具有计算值的节点,一个键使用这个生成的节点(无需进一步计算)。这种方法对于复杂的计算键值也是有效的:XPath表达式能力之外的键值

例如,此样式表:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="msxsl">
    <xsl:key name="kItemByKey" match="Item[@key]" use="@key"/>
    <xsl:template match="/">
        <xsl:variable name="vrtfFirstPass">
            <xsl:apply-templates select="/*/*/*"/>
        </xsl:variable>
        <GroupedItems>
            <xsl:apply-templates select="msxsl:node-set($vrtfFirstPass)/*"/>
        </GroupedItems>
    </xsl:template>
    <xsl:template match="Item[not(@key)]">
        <Item key="{substring-before(substring-after(ID,'_'),'_')}">
            <xsl:copy-of select="@*|node()"/>
        </Item>
    </xsl:template>
    <xsl:template match="Item[@key]"/>
    <xsl:template match="Item[generate-id()
                              = generate-id(
                                   key('kItemByKey',@key)[1]
                                )]"
                  priority="1">
        <Group id="{@key}">
            <xsl:apply-templates select="key('kItemByKey',@key)/Name"/>
        </Group>
    </xsl:template>
    <xsl:template match="Name">
        <Item>
            <xsl:value-of select="."/>
        </Item>
    </xsl:template>
</xsl:stylesheet>

输出:

<GroupedItems>
    <Group id="A">
        <Item>foo</Item>
    </Group>
    <Group id="B">
        <Item>bar</Item>
        <Item>baz</Item>
    </Group>
</GroupedItems>

福
酒吧
巴兹

注意
node-set()
扩展函数。

@Alejandro:您能否链接到您描述的两阶段“解决方案”的示例?谢谢
<GroupedItems>
    <Group id="A">
        <Item>foo</Item>
    </Group>
    <Group id="B">
        <Item>bar</Item>
        <Item>baz</Item>
    </Group>
</GroupedItems>