Выбор уникальных элементов с использованием XSLT - PullRequest
4 голосов
/ 28 апреля 2010

У меня есть следующий XML:

<option>
    <title>ABC</title>
    <desc>123</desc>
</option>
<option>
    <title>ABC</title>
    <desc>12345</desc>
</option>
<option>
    <title>ABC</title>
    <desc>123</desc>
</option>
<option>
    <title>EFG</title>
    <desc>123</desc>
</option>
<option>
    <title>EFG</title>
    <desc>456</desc>
</option>

Используя XSLT, я хочу преобразовать его в:

<choice>
    <title>ABC</title>
    <desc>123</desc>
    <desc>12345</desc>
</choice>
<choice>
    <title>EFG</title>
    <desc>123</desc>
    <desc>456</desc>
</choice>

Ответы [ 3 ]

2 голосов
/ 28 апреля 2010

Я бы посоветовал изучить "группирование" для решения этой проблемы. Либо встроенные функции группировки в XSLT 2.0, например, для каждой группы, либо, если вы используете XSLT 1, метод, называемый «группировка по Мюнхену».

1 голос
/ 28 апреля 2010

Вот минимальное решение XSLT 2.0 :

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

 <xsl:template match="/*">
  <choices>
   <xsl:for-each-group select="*/title" group-by=".">
     <choice>
       <title>
         <xsl:sequence select="current-grouping-key()"/>
       </title>
       <xsl:for-each-group select="current-group()/../desc" group-by=".">
         <xsl:sequence select="."/>
       </xsl:for-each-group>
     </choice>
   </xsl:for-each-group>
  </choices>
 </xsl:template>
</xsl:stylesheet>

Обратите внимание на использование функций current-group() и current-grouping-key()

0 голосов
/ 29 апреля 2010

Вы уже получили хорошие ответы. В погоне за краткостью я представляю 16-строчное решение, основанное на ответе Dimitres :

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

  <xsl:template match="/*">
    <choices>
      <xsl:for-each-group select="option" group-by="title">
        <choice>
          <xsl:sequence select="title"/>
          <xsl:for-each-group select="current-group()/desc" group-by=".">
            <xsl:sequence select="."/>
          </xsl:for-each-group>
        </choice>
      </xsl:for-each-group>
    </choices>
  </xsl:template>
</xsl:stylesheet>

Обратите внимание, что текущий узел контекста внутри for-each-group является первым элементом в текущей группе, а current-group() возвращает список всех элементов в текущей группе. Я использую тот факт, что элемент title идентичен для ввода и вывода, и копирую первый заголовок из каждой группы.

А для полноты, решение XSLT 1.0, использующее мюнхенскую группировку (20 строк):

<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="title" match="option/title" use="."/>
  <xsl:key name="desc" match="option/desc" use="."/>

  <xsl:template match="/*">
    <choices>
      <xsl:for-each select="option/title[count(.|key('title',.)[1]) = 1]">
        <choice>
          <xsl:copy-of select="."/>
          <xsl:for-each select="key('title',.)/../desc
              [count(.|key('desc', .)[../title=current()][1]) = 1]">
            <xsl:copy-of select="."/>
          </xsl:for-each>
        </choice>
      </xsl:for-each>
    </choices>
  </xsl:template>
</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...