2010-04-28 2 views
4

У меня есть следующий XML:Выбор уникальных элементов с помощью XSLT

<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> 
+0

Хороший вопрос (+1). См. Мой ответ на минимальное (19 строк) решение XSLT 2.0. :) –

ответ

2

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

+0

Это решило проблему, которая у меня была как 3 часа, благодаря кучам, для каждой группы работала отлично. –

+0

+1 Хороший ответ. – markusk

1

Здесь минимальное решение 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

+1 Хороший ответ. – markusk

+0

@ Dimitre, не могли бы вы подробнее рассказать о '', что это за пуповина? Я никогда не использовал его и никогда не чувствовал, что у меня что-то не хватает, но, возможно, его использование могло бы свести меня к беде? – topskip

+0

@Patrick: '' аналогичен '', однако он не создает копии узлов, выбранных в атрибуте' 'select" '. Скорее он создает что-то вроде «ссылок» на эти узлы. Подводя итог, '' является более экономичным и эффективным, чем '' –

0

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

<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 решение с использованием Muenchian группировки (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>