Подсчет отдельных элементов в XSLT и перечисление только один раз

У меня есть следующий XML:

<assessment>
    <section>
        <item>
            <attributes>
                <variables>
                    <variable>
                        <variable_name value="MORTIMER"/>
                    </variable>
                </variables>
            </attributes>
        </item>
        <item>
            <attributes>
                <variables>
                    <variable>
                        <variable_name value="FRED"/>
                    </variable>
                </variables>
            </attributes>
        </item>
        <item>
            <attributes>
                <variables>
                    <variable>
                        <variable_name value="MORTIMER"/>
                    </variable>
                </variables>
            </attributes>
        </item>
    </section>
</assessment>

У меня есть следующий XSLT для обработки этого XML:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kValueByVal" match="item//variables//variable_name" 
          use="@value"/>

 <xsl:template match="assessment">
     <xsl:for-each select="
      .//item//variables//variable_name/@value
      ">
        <xsl:value-of select=
        "concat(., ' ', count(key('kValueByVal', .)), '&#xA;')"/>
         <br/>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

Он выводит следующее, что почти то, что я хочу:

MORTIMER 2
FRED 1
MORTIMER 2

В нем перечислены все имена переменных и количество повторений каждого из них. Единственная проблема заключается в том, что он дает это количество один раз для каждого появления variable_name, а не только один раз.

Это то, что я хочу, чтобы он выводил:

MORTIMER 2
FRED 1

Как изменить код XSLT, чтобы получить это? Обратите внимание, что мы используем XSLT 1.0.

Следующее решение, которое вроде бы должно работать, ничего не выводит:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kValueByVal" match="item//variables//variable_name"
          use="@value"/>

 <xsl:template match="assessment">
     <xsl:for-each select=".//item//variables//variable_name/@value[generate-id()
                                                                    =
                                                                    generate-id(key('kValueByVal',.)[1])]">
        <xsl:value-of select=
        "concat(., ' ', count(key('kValueByVal', .)), '&#xA;')"/>
         <br/>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

person Paul Reiners    schedule 16.07.2010    source источник
comment
Мой ответ дает краткое решение.   -  person Dimitre Novatchev    schedule 16.07.2010


Ответы (4)


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

Прочтите руководство Джени Теннисон.

Вот решение вашего последнего вопроса:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kVarNameByVal" match="variable_name"
          use="@value"/>

 <xsl:template match=
  "variable_name[generate-id()
                =
                 generate-id(key('kVarNameByVal', @value)[1])
                ]
  ">
        <xsl:value-of select=
        "concat(@value, ' ', count(key('kVarNameByVal', @value)), '&#xA;')"/>
         <br/>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование выполняется с предоставленным XML-документом, получается желаемый результат:

MORTIMER 2
FRED 1
person Dimitre Novatchev    schedule 16.07.2010
comment
Наконец-то я добрался до чтения этого урока, спасибо! И теперь я, наконец, начинаю понимать этот материал. - person Paul Reiners; 19.07.2010

Поскольку вы используете XSLT 2.0, я бы использовал встроенный функции группировки. (Для более ранних версий вы, вероятно, захотите изучить мюнхианскую группировку.)

<?xml version="1.0" ?>
<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kValueByVal" match="item//variables//variable_name" 
          use="@value"/>

 <xsl:template match="assessment">
     <xsl:for-each-group select=".//item//variables//variable_name" group-by="@value">
                <xsl:value-of select="concat(current-grouping-key(), ' ', count(current-group()), '&#xA;')"/>
        <br/>
     </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>
person Matt Gibson    schedule 16.07.2010
comment
Спасибо, но при попытке сделать это я получаю следующую ошибку: Ошибка во время преобразования XSLT: преобразование XSLT не удалось. - person Paul Reiners; 16.07.2010
comment
Вот ошибки, которые я получаю: D:\software\source\applications\Athena\main\src\vue\exam\spector\xslt>xalan репозиторий.inspection-pi.xml variable.xslt XSLT Предупреждение: элемент xsl:for- каждая группа является неизвестным элементом XSLT. (variables.xslt, строка 11, столбец 87.) XPathParserException: функция «текущий ключ группировки» не найдена. выражение = 'concat(current-grouping-key(), ' ', count(current-group()), ' ')' Остальные токены: ( 'current-grouping-key' '(' ')' ',' '' '' ',' 'count ' '(' 'current-group' '(' ')' ')' ',' '' '' ')') (variables.xslt, строка 12, столбец 110) - person Paul Reiners; 16.07.2010
comment
Возможно, процессор действительно не 2.0. Есть решение 1.0? - person Paul Reiners; 16.07.2010

Это будет сделано в XSLT 1.0.

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kValueByVal" match="item//variables//variable_name" 
          use="@value"/>

 <xsl:template match="assessment">
     <xsl:for-each select="
      //item//variables//variable_name[not(@value=ancestor::item/preceding-sibling::item//variables//variable_name/@value)]
      ">
        <xsl:value-of select=
        "concat(@value, ' ', count(key('kValueByVal', @value)), '&#xA;')"/>
         <br/>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

Результат, который я получаю,

MORTIMER 2
<br />FRED 1
<br />

Обратите внимание, что он предполагает немного больше о структуре документа (бит ancestor::item), но вы сможете взять его оттуда.

person harpo    schedule 16.07.2010

Вы получаете то, о чем просите:

<xsl:for-each select=".//item//variables//variable_name/@value"> 

Что означает: для каждого из этих атрибутов

При группировке вы должны сказать: каждый из них единственный в своем роде.

И как узнать, какие из них единственные в своем роде? По мюнхенскому методу:

<xsl:for-each select=".//item//variables//variable_name/@value[generate-id()
                                                               =
                                                               generate-id(key('kValueByVal',.)[1])]">

Это означает: те, у кого этот ключ был первым.

EDIT: также избегайте //, если вы знаете схему ввода.

EDIT: теперь я вижу, что вы меняете ключ... Итак, для вас новый ключ, кто первый в своем роде? Да! variable_name элемент:

<xsl:for-each select=".//item//variables//variable_name[generate-id()
                                                        =
                                                        generate-id(key('kValueByVal',@value)[1])]">
person Community    schedule 16.07.2010
comment
@Paul Reiners: это потому, что вы изменили ключ. После небольшого изменения, которое вы вносите в свой вопрос об одной и той же таблице стилей, это сложно. - person ; 16.07.2010