Использование шаблона в генерации ключей XSLT - PullRequest
0 голосов
/ 05 февраля 2011

У меня есть XML-документ, содержащий элементы, каждый из которых имеет строку идентифицированного токена.

<?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-документ, который группирует элементы в соответствии с 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 IЯ бы просто определил функцию для этого, но я застрял с XSLT 1.0 из-за ограничений инструментов, которые я не могу контролировать.

Ответы [ 2 ]

1 голос
/ 07 февраля 2011

Во-первых, если вы посмотрите на все ответы здесь с помощью метода группировки Мюнхена, вы увидите, что каждый из них «дублирует» вычисление значения ключа.

Единственным «решением» для этого является двухфазное преобразование: один шаблон добавляет узел с вычисляемым значением только к входному источнику и ключ, использующий этот сгенерированный узел (дальнейших вычислений нет). Этот метод также действителен со сложным вычисленным значением ключа: значения, выходящие за рамки выражений 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() функция расширения.

1 голос
/ 05 февраля 2011

С vanilla XSLT 1.0 вы застряли с дублированием кода, если хотите использовать метод группировки по мюнхенскому. Решения, которые смогут использовать шаблоны при извлечении ключей, будут значительно медленнее и менее читабельными.

...