Как выбрать уникальные узлы - PullRequest
14 голосов
/ 23 октября 2008

Я нашел эту страницу , описывающую метод Мюнхена, но я думаю, что я применяю его неправильно.

Учтите, что это вернет набор возрастов:

/doc/class/person/descriptive[(@name='age')]/value

1..2..2..2..3..3..4..7

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

1..2..3..4..7

Кажется, что каждое из них возвращает все значения вместо уникальных значений:

/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::value)]/value
/doc/class/person/descriptive[(@name='age')]/value[not(value=preceding-sibling::value)]

Чего мне не хватает?

Ответы [ 5 ]

23 голосов
/ 23 октября 2008

Вот пример:

<root>
    <item type='test'>A</item>
    <item type='test'>B</item>
    <item type='test'>C</item>
    <item type='test'>A</item>
    <item type='other'>A</item>
    <item type='test'>B</item>
    <item type='other'>D</item>
    <item type=''>A</item>
</root>

И XPath:

//preceding::item/preceding::item[not(.=preceding-sibling::item)]/text()

Результаты: A B C D

EDIT : Как заметил Мусио, это не захватывает последний элемент в списке, если это единственный раз, когда он появляется. Принимая это во внимание и комментарий Феанора, вот лучшее решение:

/root/item[not(.=preceding-sibling::item)]
14 голосов
/ 23 октября 2008

Вот мюнхенская версия ответа БК с использованием его данных:

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

  <xsl:output indent="yes" method="text"/>
  <xsl:key name="item-by-value" match="item" use="."/>

  <xsl:template match="/">
    <xsl:apply-templates select="/root/item"/>
  </xsl:template>

  <xsl:template match="item">
    <xsl:if test="generate-id() = generate-id(key('item-by-value', normalize-space(.)))">
      <xsl:value-of select="."/>
      <xsl:text>
</xsl:text>
    </xsl:if>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:apply-templates/>
  </xsl:template>
</xsl:stylesheet>

Это преобразование дает

A
B
C
D

  1. Приведенный выше поиск key() в шаблоне для item возвращает набор узлов, содержащий все элементы item с тем же строковым значением, что и у контекстного узла.
  2. Если вы примените функцию, которая ожидает один узел к набору узлов, она будет работать на первом узле в этом наборе узлов.
  3. Все вызовы generate-id() гарантированно генерируют один и тот же идентификатор для данного узла в течение одного прохода через документ.
  4. Следовательно, тест будет верным, если узел контекста будет тем же узлом, что и первый узел, возвращенный вызовом key().
3 голосов
/ 21 января 2014

Для тех, кто все еще ищет отдельное в XSLT:

С XSLT 2.0, ты можешь использовать "Отчетливые-значения (/ документ / класс / человек / описательный [(@ имя = 'возраст')] / значение)"

2 голосов
/ 11 июля 2009

Метод Мюнхена использует ключи для создания уникального списка элементов из набора узлов. Для ваших данных ключ будет выглядеть так:

<!-- Set the name to whatever you want -->
<xsl:key name="PeopleAges" match="/doc/class/person/descriptive[@name = 'age']/value" use="." />

Оттуда я лично использовал бы xsl:apply-templates, но вы можете использовать следующий атрибут select в других местах:

<!-- you can change `apply-templates` to: `copy-of` or `for-each`. -->
<xsl:apply-templates select="/doc/class/person/descriptive[@name = 'age']/value[count(. | key('PeopleAges', .)[1]) = 1]" />

Сопоставление с вышеперечисленным намного проще:

<xsl:template match="person/descriptive[@name = 'age']/value">
    <strong>Age: </strong><xsl:value-of select="." />
</xsl:template>
1 голос
/ 23 октября 2008

Не упускаете ли вы ссылку на «описательный» сразу после предыдущего значения? Что-то вроде следующего:

/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::descriptive[@name='age']/value)]/value

(еще не проверял)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...