2009-11-18 4 views
2

Я пытаюсь использовать группировку Muenchian в своем XSLT для группировки совпадающих узлов, но я хочу только группировать внутри родительского узла, а не по всему исходному XML-документу ,Muenchian Grouping - группа внутри узла, а не во всем документе

Учитывая XSLT и XML следующим образом (извинения за длину моего образца кода):

XSLT

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> 
<xsl:output method="html" indent="yes"/> 

<xsl:key name="contacts-by-surname" match="contact" use="surname" /> 
<xsl:template match="records"> 
    <xsl:for-each select="contact[count(. | key('contacts-by-surname', surname)[1]) = 1]"> 
    <xsl:sort select="surname" /> 
    <xsl:value-of select="surname" />,<br /> 
    <xsl:for-each select="key('contacts-by-surname', surname)"> 
    <xsl:sort select="forename" /> 
    <xsl:value-of select="forename" /> (<xsl:value-of select="title" />)<br /> 
    </xsl:for-each> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

XML

<root> 
<records> 
    <contact id="0001"> 
    <title>Mr</title> 
    <forename>John</forename> 
    <surname>Smith</surname> 
    </contact> 
    <contact id="0002"> 
    <title>Dr</title> 
    <forename>Amy</forename> 
    <surname>Jones</surname> 
    </contact> 
    <contact id="0003"> 
    <title>Mrs</title> 
    <forename>Mary</forename> 
    <surname>Smith</surname> 
    </contact> 
    <contact id="0004"> 
    <title>Ms</title> 
    <forename>Anne</forename> 
    <surname>Jones</surname> 
    </contact> 
    <contact id="0005"> 
    <title>Mr</title> 
    <forename>Peter</forename> 
    <surname>Smith</surname> 
    </contact> 
    <contact id="0006"> 
    <title>Dr</title> 
    <forename>Indy</forename> 
    <surname>Jones</surname> 
    </contact> 
</records> 
<records> 
    <contact id="0001"> 
    <title>Mr</title> 
    <forename>James</forename> 
    <surname>Smith</surname> 
    </contact> 
    <contact id="0002"> 
    <title>Dr</title> 
    <forename>Mandy</forename> 
    <surname>Jones</surname> 
    </contact> 
    <contact id="0003"> 
    <title>Mrs</title> 
    <forename>Elizabeth</forename> 
    <surname>Smith</surname> 
    </contact> 
    <contact id="0004"> 
    <title>Ms</title> 
    <forename>Sally</forename> 
    <surname>Jones</surname> 
    </contact> 
    <contact id="0005"> 
    <title>Mr</title> 
    <forename>George</forename> 
    <surname>Smith</surname> 
    </contact> 
    <contact id="0006"> 
    <title>Dr</title> 
    <forename>Harry</forename> 
    <surname>Jones</surname> 
    </contact> 
</records> 
</root> 

РЕЗУЛЬТАТ

Jones, 
Amy (Dr) 
Anne (Ms) 
Harry (Dr) 
Indy (Dr) 
Mandy (Dr) 
Sally (Ms) 

Smith, 
Elizabeth (Mrs) 
George (Mr) 
James (Mr) 
John (Mr) 
Mary (Mrs) 
Peter (Mr) 

Как я группа внутри каждого <records> и достижения этого результата:

Jones, 
Amy (Dr) 
Anne (Ms) 
Indy (Dr) 

Smith, 
John (Mr) 
Mary (Mrs) 
Peter (Mr) 

Jones, 
Harry (Dr) 
Mandy (Dr) 
Sally (Ms) 

Smith, 
Elizabeth (Mrs) 
George (Mr) 
James (Mr) 
+1

+1 Хороший вопрос –

+0

Kristian, в вашем желаемом результате, первые имена не сортируется в фамилиях. Я предполагаю, что они должны быть, поскольку вы явно сортируете по имени в вашем xslt. –

+0

Хороший вопрос о заказе - обновил вопрос, чтобы отсортировать имена в результатах. – kristian

ответ

6

Взял меня некоторое время ... Я собирался отказаться, но продолжал все-таки :)

Недостатком ключевая функция заключается в том, что сгенерированный ключ всегда будет для всего xml. Следовательно, вы должны конкатенировать дополнительную информацию в своем ключе, чтобы сделать ее более конкретной. В частности. ниже, я объединяю позицию узла записей, так что я получаю ключи для разных фамилий на записи.

Вот XSLT:

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> 
    <xsl:output method="html" indent="yes"/> 
    <xsl:key name="distinct-surname" match="contact" use="concat(generate-id(..), '|', surname)"/> 
    <xsl:template match="records"> 
    <xsl:for-each select="contact[generate-id() = generate-id(key('distinct-surname', concat(generate-id(..), '|', surname))[1])]"> 
     <xsl:sort select="surname" /> 
     <xsl:value-of select="surname" />,<br /> 
     <xsl:for-each select="key('distinct-surname', concat(generate-id(..), '|', surname))"> 
     <xsl:sort select="forename" /> 
     <xsl:value-of select="forename" /> (<xsl:value-of select="title" />)<br /> 
     </xsl:for-each> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

Это результат:

Jones, 
Amy (Dr) 
Anne (Ms) 
Indy (Dr) 
Smith, 
John (Mr) 
Mary (Mrs) 
Peter (Mr) 
Jones, 
Harry (Dr) 
Mandy (Dr) 
Sally (Ms) 
Smith, 
Elizabeth (Mrs) 
George (Mr) 
James (Mr) 

Пожалуйста, обратите внимание, что результат отсортирован по слишком отчество. Если вы не хотите, чтобы отсортировать его отчество, вам нужно удалить строку <xsl:sort select="forename" />

+0

Отличный ответ и объяснение. Благодаря! – kristian

+0

Вот что я бы сделал, +1. Я предлагаю крошечное изменение: вместо 'concat (count (parent :: */previous-sibling :: *), фамилия)', используйте 'concat (generate-id (..), '|', surname)'. Он короче, эффективнее и немного безопаснее из-за дополнительного разделителя. – Tomalak

+0

Tomalak, я отредактировал xslt согласно вашему предложению. Спасибо :) –

3

Существует простой метод, добавив предикат, которые обеспечивают чем контактов, участвующие в испытании Мюнха являются ребенок текущие записей ,

<xsl:key name="contacts-by-surname" match="contact" use="surname" /> 
<xsl:template match="records"> 
    <xsl:for-each select="contact[count(. | key('contacts-by-surname', surname)[generate-id(parent::records) = generate-id(current())][1]) = 1]"> 
    <xsl:sort select="surname" /> 
    <xsl:value-of select="surname" />,<br /> 
    <xsl:for-each select="key('contacts-by-surname', surname)[generate-id(parent::records) = generate-id(current()/parent::records)]"> 
    <xsl:sort select="forename" /> 
    <xsl:value-of select="forename" /> (<xsl:value-of select="title" />)<br /> 
    </xsl:for-each> 
    </xsl:for-each> 
</xsl:template> 
+0

Это может быть проще, но он также менее эффективен. Я бы сказал, что «contact [generate-id() = generate-id (... [...])]' is O (n²) в худшем случае, а @Rashmi Pandit 'contact [generate-id() = generate-id (...)] является O (n). – Tomalak

+0

Возможно, менее эффективный, но более надежный, я думаю. Сжатие строк в составные ключи подразумевает, что строка разделителя никогда не встречается в любой используемой строке. Я предпочитаю детерминированное поведение быстрее всего. :) – Erlock

+0

Хм ... Я могу думать о том, что значение id неоднозначно (id '" key-30 "', value '" 0 "' vs.id '" key-300 "', value '" "'), но для id-separator-value (это будет '' id-30 | 0 "' vs. 'id-300 |" ')? Наличие разделителя в значении не имеет значения, ИМХО. Я что-то упускаю? – Tomalak