Как выбрать уникальные узлы
Я нашел эту страницу, описывающий метод Muenchian, но я думаю, что я применяю его неправильно.
Учтите, что это вернет множество возрастов:
/doc/class/person/descriptive[(@name='age')]/value
1..2..2..2..3..3..4..7
Но я хотел бы, чтобы nodeet установил только один node для каждого возраста.
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)]
Что мне не хватает?
Ответы
Ответ 1
Вот пример:
<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
ИЗМЕНИТЬ:
Как прокомментировал mousio, это не фиксирует последний элемент в списке, если это единственный раз, когда он появляется. Принимая во внимание это и комментарии Fëanor, здесь лучшее решение:
/root/item[not(.=preceding-sibling::item)]
Ответ 2
Вот мюнхенская версия ответа BQ, используя его данные:
<?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>
Это преобразование дает
а
B
C
D
- Образец
key()
выше в шаблоне для item
возвращает узел узлов, содержащий все элементы item
с тем же строковым значением, что и контекст node.
- Если вы применяете функцию, ожидающую единственного node к набору узлов, он будет работать с первым node в этом узле.
- Все вызовы
generate-id()
гарантируют получение одинакового идентификатора для данного node в течение одного прохода через документ.
- Поэтому тест будет истинным, если контекст node является тем же самым node, что и первый, возвращенный вызовом
key()
.
Ответ 3
Для тех, кто все еще ищет выделение в XSLT:
С XSLT 2.0,
вы можете использовать
"Отчетливое-значение (/документ/класс/человек/описательный [(@name= 'возраст')]/значение)"
Ответ 4
Метод Muenchian использует ключи для создания уникального списка элементов из набора node. Для ваших данных ключ будет выглядеть так:
<!-- 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>
Ответ 5
Вам не хватает ссылки на "описательный" сразу после предыдущего значения? Что-то вроде следующего:
/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::descriptive[@name='age']/value)]/value
(Не проверял его)