Ходить / перебирать XSL-ключ: как? - PullRequest
2 голосов
/ 03 мая 2010

Есть ли способ пройти ключ и вывести все содержащиеся в нем значения?

   <xsl:key name="kElement" match="Element/Element[@idref]" use="@idref" />

Я думал об этом так:

<xsl:for-each select="key('kElement', '.')">
  <li><xsl:value-of select="." /></li>
</xsl:for-each>

Однако это не работает. Я просто хочу перечислить все значения в ключе для целей тестирования.

Вопрос просто: как это можно сделать?

Ответы [ 4 ]

13 голосов
/ 03 мая 2010

Вы не можете. Это не то, для чего нужны ключи.

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

Если вам нужно перебрать все, для чего определен ключ, вы можете использовать выражение в атрибуте match="..." вашего <key> элемента.

Так что, если у вас есть файл, подобный этому:

<root>
  <element name="Bill"/>
  <element name="Francis"/>
  <element name="Louis"/>
  <element name="Zoey"/>
</root>

И ключ определяется так:

<xsl:key name="survivors" match="element" use="@name"/>

Вы можете просмотреть, что ключ использует, используя содержимое его атрибута match:

<xsl:for-each select="element">
  <!-- stuff -->
</xsl:for-each>

В качестве альтернативы, если у каждого элемента есть что-то общее:

<root>
  <element name="Bill" class="survivor"/>
  <element name="Francis" class="survivor"/>
  <element name="Louis" class="survivor"/>
  <element name="Zoey" class="survivor"/>
</root>

Тогда вы можете определить свой ключ следующим образом:

<xsl:key name="survivors" match="element" use="@class"/>

И переберите все элементы следующим образом:

<xsl:for-each select="key('survivors', 'survivor')">
  <!-- stuff -->
</xsl:for-each>

Поскольку каждый элемент разделяет значение «уцелевший» для атрибута class.

В вашем случае ваш ключ

<xsl:key name="kElement" match="Element/Element[@idref]" use="@idref" />

Так что вы можете просмотреть все, что у него есть, вот так:

<xsl:for-each select="Element/Element[@idref]">
  <!-- stuff -->
</xsl:for-each>
5 голосов
/ 15 мая 2013

Вы МОЖЕТ создать ключ для использования в цикле - если просто указать константу в атрибуте использования элемента ключа:

<xsl:key name="survivors" match="element" use="'all'"/>

Затем вы можете перебрать все элементы следующим образом:

<xsl:for-each select="key('survivors','all')">
    ...
</xsl:for-each>

Или посчитайте их:

<xsl:value-of select="count(key('survivors','all'))"/>

Обратите внимание, что константа может быть любой строкой или даже числом, но «все» читается хорошо.

Однако вы не можете использовать этот ключ для поиска информации об отдельных записях (поскольку все они имеют один и тот же ключ).

Другими словами, существует два типа возможных ключей:

  1. "ключи поиска" = стандартные ключи с различными индексами в атрибуте использования
  2. "looping keys" = ключи с константой в атрибуте использования

Я не знаю, насколько эффективен этот метод, однако он делает обслуживание XSL более эффективным, избегая повторения одного и того же (потенциально очень сложного) выражения XPath во всем коде XSL.

2 голосов
/ 22 августа 2014

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

<xsl:key name="paths" match="path" use="keygenerator()">

это может быть "повторено" / "проход", как показано ниже

<xsl:for-each select="//path[generate-id()=generate-id(key('paths',keygenerator())[1])]">

Чтобы понять это магическое число [1], давайте рассмотрим следующий пример:

Рассмотрим этот фрагмент XML

<root>
    <Person>
        <name>Johny</name>
        <date>Jan10</date>
        <cost itemID="1">34</cost>
        <cost itemID="1">35</cost>
        <cost itemID="2">12</cost>
        <cost itemID="3">09</cost>
    </Person>
    <Person>
        <name>Johny</name>
        <date>Jan09</date>
        <cost itemID="1">21</cost>
        <cost itemID="1">41</cost>
        <cost itemID="2">11</cost>
        <cost itemID="2">14</cost>
    </Person>
</root>

преобразовано с использованием этого XSL.

  <xsl:for-each select="*/Person">
  <personrecords>
       <xsl:value-of select="generate-id(.)" />--
       <xsl:value-of select="name"/>--
       <xsl:value-of select="date"/>--      
  </personrecords>
  </xsl:for-each>

  <xsl:for-each select="*/*/cost">
  <costrecords>
      <xsl:value-of select="generate-id(.)" />--
      <xsl:value-of select="../name"/>-- 
      <xsl:value-of select="../date"/>-- 
      <xsl:value-of select="@itemID"/>-- 
      <xsl:value-of select="text()"/>
  </costrecords>
  </xsl:for-each>

Приведенное выше преобразование XSL перечисляет уникальный идентификатор узлов Person и узлов cost в форме idpxxxxxxx, как показано в результате ниже.

  1. <personrecords>idp2661952--Johny--Jan10--      </personrecords>
  2. <personrecords>idp4012736--Johny--Jan09--      </personrecords>

  3. <costrecords>idp2805696--Johny-- Jan10-- 1-- 34</costrecords>
  4. <costrecords>idp4013568--Johny-- Jan10-- 1-- 35</costrecords>
  5. <costrecords>idp2808192--Johny-- Jan10-- 2-- 12</costrecords>
  6. <costrecords>idp2808640--Johny-- Jan10-- 3-- 09</costrecords>
  7. <costrecords>idp2609728--Johny-- Jan09-- 1-- 21</costrecords>
  8. <costrecords>idp4011648--Johny-- Jan09-- 1-- 41</costrecords>
  9. <costrecords>idp2612224--Johny-- Jan09-- 2-- 11</costrecords>
  10.<costrecords>idp2610432--Johny-- Jan09-- 2-- 14</costrecords>

Давайте создадим ключ для записей cost, используя комбинацию значений name и itemID.

 <xsl:key name="keyByNameItem" match="cost" use="concat(../name, '+', @itemID)"/>

При просмотре XML вручную количество уникальных ключей для перечисленного выше будет равно трем: Джони + 1 , Джони + 2 и Джони + 3 .

Теперь давайте проверим этот ключ, используя приведенный ниже фрагмент.

   <xsl:for-each select="*/*/cost">
   <costkeygroup>
      <xsl:value-of select="generate-id(.)" />-- 
      (1)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', @itemID) )[1] ) " />-- 
      (2)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', @itemID) )[2] ) " />-- 
      (3)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', @itemID) )[3] ) " />-- 
      (4)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', @itemID) )[4] ) " />
    </costkeygroup>
    </xsl:for-each>

А вот и результат:

  1. <costkeygroup>idp2805696-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
  2. <costkeygroup>idp4013568-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
  3. <costkeygroup>idp2808192-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
  4. <costkeygroup>idp2808640-- (1)idp2808640-- (2)-- (3)-- (4)</costkeygroup>
  5. <costkeygroup>idp2609728-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
  6. <costkeygroup>idp4011648-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
  7. <costkeygroup>idp2612224-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
  8. <costkeygroup>idp2610432-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>

Мы заинтересованы в том, чтобы понять важность [1], [2], [3], [4]. В нашем случае ключевой генератор - concat(../name, '+', @itemID).

Для данного ключа [1] относится к первому вхождению узла, который удовлетворяет генератору ключей. Точно так же [2] относится к второму вхождению узла, который удовлетворяет генератору ключей. Таким образом, [2], [3], [4] и т. Д. Являются узлами, которые удовлетворяют одному и тому же ключу, и, таким образом, могут рассматриваться как дубликаты для данного ключа. Количество дубликатов зависит от входного XML. Таким образом:

Ключ Джонни + 1 удовлетворяет 4 узлам (1) idp2805696-- (2) idp4013568-- (3) idp2609728-- (4) idp4011648
Ключ Джони + 2 удовлетворяет 3 узлам (1) idp2808192-- (2) idp2612224-- (3) idp2610432-- (4)
Ключ Джони + 3 удовлетворяет 1 узел (1) idp2808640-- (2) - (3) - (4)

Таким образом, мы видим, что ВСЕ 8 cost узлов XML могут быть доступны через ключ.

Вот изображение, которое объединяет результаты преобразования, чтобы лучше понять.

enter image description here

Красные квадраты обозначают совпадающие узлы для Джонни + 1 . Зеленые квадраты обозначают совпадающие узлы для Джони + 3 . Сопоставьте значения idpxxxxxxx в <costkeygroup> со значениями в <costrecords>. <costrecords> помогает сопоставить значения idpxxxxxxx с исходным XML.

Еда на вынос это,

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

Чтобы "пройти" только уникальные узлы ключа в приведенном выше примере, используйте

 <xsl:for-each select="*/*/workTime[generate-id()=generate-id(key('keyByNameItem', concat(../name, '+', @itemID) )[1] ) ] ">

[1] означает, что первая запись для данного значения ключа обозначается как уникальная запись. [1] почти всегда используется, потому что будет существовать хотя бы один узел, который удовлетворяет данному значению ключа. Если мы уверены, что для каждого значения ключа в ключе будет минимум 2 записи, мы можем пойти дальше и использовать [2] для идентификации второй записи в наборе записей как уникальной записи.

P.S. Слова узлы / записи / элементы используются взаимозаменяемо.

0 голосов
/ 11 февраля 2015

Нет способа пройтись по клавишам, хотя мы можем вывести все содержащиеся в нем значения. В XSLT2 это гораздо проще, чем в XSLT1 (например, используя fn:generate-id в соответствии с предыдущим ответом).

Использование fn:distinct-values

<xsl:variable name="e" select="."/>
<xsl:for-each select="distinct-values(Element/Element[@idref]/@idref)">
  <li key="{.}"><xsl:value-of select="key('kElement', ., $e )" /></li>
</xsl:for-each>

Использование xsl:for-each-group

<xsl:for-each-group select="Element/Element[@idref]" group-by="@idref">
  <li key="{current-grouping-key()}"><xsl:value-of select="current-group()" /></li>
</xsl:for-each-group>
...