在XSLT密钥生成中使用模板

时间:2011-02-05 04:27:14

标签: 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>
        <Name>bar</Name>
    </Item>
    <Item>
        <ID>1_B_7</ID>
        <Name>baz</Name>
    </Item>
</Items>
</Test>

我需要将其转换为另一个XML文档,该文档根据ID字符串的 middle 部分(上例中的字母)对项目进行分组。

<?xml version='1.0' ?>
<GroupedItems>
    <Group id="A">
        <Item>foo</Item>
    </Group>
    <Group id="B">
        <Item>bar</Item>
        <Item>baz</Item>
    </Group>
</GroupedItems>

我正在使用按键功能找到群组:

<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。

2 个答案:

答案 0 :(得分:1)

如果你想使用Muenchian分组方法,使用vanilla XSLT 1.0就会遇到代码重复问题。能够在密钥提取中使用模板的解决方案会慢得多,而且可读性也会降低。

答案 1 :(得分:1)

首先,如果您使用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()扩展功能。