Muenchian Grouping - группа внутри узла, а не внутри всего документа - PullRequest
2 голосов
/ 18 ноября 2009

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

Учитывая 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)

Ответы [ 2 ]

6 голосов
/ 18 ноября 2009

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

Недостатком функции key является то, что сгенерированный ключ всегда будет для всего 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" />

3 голосов
/ 18 ноября 2009

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

<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>
...