Как определить дублирующие узлы в XPath 1.0 с помощью XPathNavigator для оценки?

Я пытаюсь идентифицировать повторяющиеся серийные номера из следующего xml, используя XPath 1.0, а затем оценивая его в .Net с помощью XPathNavigator.

<!--?xml version="1.0" encoding="utf-16"?-->
<inventory xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <items>
 <item>
 <serialnumber>1111</serialnumber>
 </item>
 <item>
 <serialnumber>1112</serialnumber>
 </item>
 <item>
 <serialnumber>1112</serialnumber>
 </item>
 </items>
</inventory>

Я попытался сделать это, оценив это

//Items/Item/SerialNumber

в пользовательской контекстной функции XSLT (реализация IXsltContextFunction как этот пример MSDN) в .Net, но функция Invoke получает один результат за один раз, поэтому у меня нет видимости других результатов, чтобы найти дубликаты.

1) Есть ли способ сделать это, используя одно выражение XPath 1.0?

ИЛИ

2) Есть ли способ передать в массиве элементов один вызов Invoke пользовательского класса контекстной функции XSLT? Я работаю в VB.Net, но я доволен любым С# примеры, которые могут поделиться.

Спасибо,

Гэвин

Edit

Благодаря O R Mapper и Dimitre для их ответов. Сначала я принял ответ O R Mapper, так как он сделал то, что я спросил. С тех пор я принял решение Dimitre, так как мне нравится, что он предоставляет отличный список значений. Оба ответа очень полезны, хотя!

2 ответа

Использование

/*/*/Item
 [SerialNumber = following-sibling::Item/SerialNumber
 and
 not(SerialNumber = preceding-sibling::Item/SerialNumber)
 ]

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

Проверка на основе XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes">
 <xsl:strip-space elements="*">
 <xsl:template match="/">
 <xsl:copy-of select="/*/*/Item
 [SerialNumber = following-sibling::Item/SerialNumber
 and
 not(SerialNumber = preceding-sibling::Item/SerialNumber)
 ]">
 </xsl:copy-of></xsl:template>
</xsl:strip-space></xsl:output></xsl:stylesheet>

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

<inventory>
 <items>
 <item>
 <serialnumber>1111</serialnumber>
 </item>
 <item>
 <serialnumber>2222</serialnumber>
 </item>
 <item>
 <serialnumber>2222</serialnumber>
 </item>
 <item>
 <serialnumber>2222</serialnumber>
 </item>
 <item>
 <serialnumber>1111</serialnumber>
 </item>
 <item>
 <serialnumber>1111</serialnumber>
 </item>
 <item>
 <serialnumber>3333</serialnumber>
 </item>
 </items>
</inventory>

преобразование оценивает выражение XPath и копирует выбранные узлы в выходной файл:

<item>
 <serialnumber>1111</serialnumber>
</item>
<item>
 <serialnumber>2222</serialnumber>
</item>

Наконец, если вы хотите получить только повторяющиеся значения SerialNumber, используйте:

/*/*/Item
 [SerialNumber = following-sibling::Item/SerialNumber
 and
 not(SerialNumber = preceding-sibling::Item/SerialNumber)
 ]
 /SerialNumber/text()


Я отвечу 1), поэтому 2) больше не имеет значения:

Вы можете использовать ось preceding-sibling на своих элементах , чтобы найти любые предыдущие элементы с тем же серийным номером.

Попробуйте это (написано так, чтобы оно возвращало только сами серийные номера, а не элементы - если это не совсем то, что вы хотите, и вы не знаете, как изменить результат, сообщите мне):

/Inventory/Items/Item/SerialNumber/node()[.=../../preceding-sibling::Item/SerialNumber/node()]

Для вашего образца документа он возвращает

1112

licensed under cc by-sa 3.0 with attribution.