Выбор уникальных записей в XSLT/XPath

Я должен выбрать только уникальные записи из XML-документа в контексте <xsl:for-each> цикл. Я ограничен Visual Studio использованием XSL 1.0.

    <availList>
        <item>
          <schDate>2010-06-24</schDate>              
          <schFrmTime>10:00:00</schFrmTime>
          <schToTime>13:00:00</schToTime>
          <variousOtherElements></variousOtherElements>
        </item>
        <item>
          <schDate>2010-06-24</schDate>              
          <schFrmTime>10:00:00</schFrmTime>
          <schToTime>13:00:00</schToTime>
          <variousOtherElements></variousOtherElements>
        </item>
        <item>
          <schDate>2010-06-25</schDate>              
          <schFrmTime>10:00:00</schFrmTime>
          <schToTime>12:00:00</schToTime>
          <variousOtherElements></variousOtherElements>
        </item>
        <item>
          <schDate>2010-06-26</schDate>              
          <schFrmTime>13:00:00</schFrmTime>
          <schToTime>14:00:00</schToTime>
          <variousOtherElements></variousOtherElements>
        </item>
        <item>
          <schDate>2010-06-26</schDate>              
          <schFrmTime>10:00:00</schFrmTime>
          <schToTime>12:00:00</schToTime>
          <variousOtherElements></variousOtherElements>
        </item>
    </availList>

Уникальность должна быть основана на значении трех дочерних элементов: schDate, schFrmTime и schToTime. Если два item элементы имеют те же значения для всех трех дочерних элементов, они - дубликаты. В вышеупомянутом XML объекты один и два являются дубликатами. Остальные уникальны. Как обозначено выше, каждый объект содержит другие элементы, которые мы не хотим включать в сравнение. 'Уникальность' должна быть фактором тех трех элементов и одних.

Я попытался выполнить это через следующее:

availList/item[not(schDate = preceding:: schDate and schFrmTime = preceding:: schFrmTime and schToTime = preceding:: schToTime)]

Идея позади этого состоит в том, чтобы выбрать записи, где нет никакого предыдущего элемента с тем же schDate, schFrmTime и schToTime. Однако его вывод пропускает последний объект. Это вызвано тем, что мой XPath на самом деле исключает объекты, где все дочерние значения элемента подобраны в рамках всего предыдущего документа. Никакой сингл item исключены соответствия все дочерние элементы последнего объекта - но потому что значение каждого элемента индивидуально присутствует в другом объекте, последний объект.

Я мог получить корректный результат путем сравнения всех дочерних значений как сцепленной строки к тем же связанным значениям для каждого предыдущего объекта. Кто-либо знает о способе, которым я мог сделать это?

5
задан Daniel Situnayake 11 June 2010 в 00:53
поделиться

2 ответа

I. Как одно выражение XPath:

/*/item[normalize-space() and not(. = preceding-sibling::item)]

II. Более эффективная (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:key name="kItemByVal" match="item" use="."/>

 <xsl:template match="/">
  <xsl:copy-of select=
   "*/item[generate-id() = generate-id(key('kItemByVal', .))]
   "/>
 </xsl:template>
</xsl:stylesheet>

И I, и II, при применении к предоставленному XML-документу правильно выбирают/копируют следующие узлы:

<item><schDate>2010-06-24</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>13:00:00</schToTime></item>
<item><schDate>2010-06-25</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>12:00:00</schToTime></item>
<item><schDate>2010-06-26</schDate><schFrmTime>13:00:00</schFrmTime><schToTime>14:00:00</schToTime></item>
<item><schDate>2010-06-26</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>12:00:00</schToTime></item>

Update: В случае если имеет других детей, то это преобразование:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:key name="kItemBy3Children" match="item"
     use="concat(schDate, '+', schFrmTime, '+', schToTime)"/>

 <xsl:template match="/">
       <xsl:copy-of select=
        "*/item[generate-id()
              = generate-id(key('kItemBy3Children',
                                concat(schDate,
                                       '+', schFrmTime,
                                       '+', schToTime)
                               )
                            )
               ]
        "/>
 </xsl:template>
</xsl:stylesheet>

дает желаемый результат.

4
ответ дан 14 December 2019 в 13:26
поделиться

Техника, которую я видел, заключается в том, чтобы сделать это за два прохода: отсортировать элементы по всем трем ключевым полям, а затем сравнить каждый элемент с предыдущим элементом (вместо всех предыдущих элементов).

Практично ли для вас выполнять два отдельных преобразования? Это значительно упрощает задачу.

Я видел эту технику в старом издании книги Michael Kay's XSLT. Вы можете найти ее в некоторых его примерах кода.

2
ответ дан 14 December 2019 в 13:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: