2012-10-08 3 views
5

Как работает Muenchian grouping?Применение группировки Muenchian для простого XML с XSLT

У меня есть простой документ XML генерируется из базы данных:

<CLIENTS> 
    <CLIENT> 
     <NAME>John</NAME> 
     <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER> 
     <LAST_USED>2012-10-03</LAST_USED> 
     <AMOUNT>5000</AMOUNT> 

    </CLIENT> 
    <CLIENT> 
     <NAME>John</NAME> 
     <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER> 
     <LAST_USED>2012-10-02</LAST_USED> 
     <AMOUNT>10000</AMOUNT> 
    </CLIENT> 
     ... 

я хотел бы, чтобы группа узла имен. Как я могу получить желаемый результат?

<ClIENTS> 
    <CLIENT> 
     <NAME>John</NAME> 
     <ACCOUNT> 
      <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER> 
      <LAST_USED>2012-10-03</LAST_USED> 
      <AMOUNT>5000</AMOUNT> 
     </ACCOUNT> 
     <ACCOUNT> 
      <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER> 
      <LAST_USED>2012-10-03</LAST_USED> 
      <AMOUNT>10000</AMOUNT> 
     </ACCOUNT> 
     .... 
</CLIENTS> 

ответ

8

Read www.jenitennison.com/xslt/grouping/muenchian.xml, для справки с кодом определить ключ

<xsl:key name="client-by-name" match="CLIENT" use="NAME"/> 

затем использовать шаблоны, как

<xsl:template match="@* | node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@* | node()"/> 
    </xsl:copy> 
</xsl:template> 


<xsl:template match="CLIENTS"> 
    <xsl:copy> 
    <xsl:apply-templates select="CLIENT[generate-id() = generate-id(key('client-by-name', NAME)[1])]" mode="group"/> 
    <xsl:copy> 
</xsl:template> 

<xsl:template match="CLIENT" mode="group"> 
    <xsl:copy> 
    <xsl:copy-of select="NAME"/> 
    <xsl:apply-templates select="key('client-by-name', NAME)"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="CLIENT"> 
    <ACCOUNT> 
    <xsl:apply-templates select="node()[not(self::NAME)]"/> 
    </ACCOUNT> 
</xsl:template> 

[редактировать] Если вы хотите использовать XSLT 2.0, то из Конечно, вам не нужна группа Muenchian, вместо этого вы используете

<xsl:template match="@* | node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@* , node()"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="CLIENTS"> 
    <xsl:copy> 
    <xsl:for-each-group select="CLIENT" group-by="NAME"> 
     <CLIENT> 
     <xsl:apply-templates select="NAME, current-group()"/> 
     </CLIENT> 
    </xsl:for-each-group> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="CLIENT"> 
    <ACCOUNT> 
    <xsl:apply-templates select="node() except NAME"/> 
    </ACCOUNT> 
</xsl:template> 
+0

Большое спасибо за полезные ответы ребята. Можно ли решить ту же проблему с xslt 2.0 xls: for-each-group? Если да, можете ли вы отправить решение? – user1728778

+0

@ пользователь1728778 - да, это возможно. Пожалуйста, см. Мой ответ на компактное решение XSLT 2.0. – ABach

4

Джени Теннисон ломает шаги, необходимые для выполнения Muenchian группировки здесь:

http://www.jenitennison.com/xslt/grouping/muenchian.html

По существу, использовать XSLT, чтобы назначить ключ к узлу, этот ключ может быть повторен, если есть одинаковые узлы в документе. Затем XSLT проходит через каждую клавишу и позволяет выводить узлы с соответствующими ключами.

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

<xsl:key name="client-by-name" match="CLIENT" use="NAME"/> 

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

<xsl:apply-templates select="CLIENT[generate-id() = generate-id(key('client-by-name', NAME)[1])]" mode="group"/> 

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

<xsl:apply-templates select="key('client-by-name', NAME)"/> 

Отсюда вам нужен еще один шаблон для вывода клиентских

3

Muenchian Группировки (в соответствии с @ ответом Мартина) устраняет избыточность, что более традиционная стратегия группировки в то время как группировке.

Без Muenchian Группировки, шаблоны обычно используют preceding-sibling или following-sibling для определения первого кандидата экземпляр каждой группы, а затем потребуется второй запрос для поиска всех узлов, соответствующих групп, следующим образом:

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

    <xsl:template match="CLIENTS"> 
     <CLIENTS> 
      <!--Only find the 'first' instance of each client--> 
      <xsl:apply-templates select="CLIENT[not(NAME = preceding-sibling::CLIENT/NAME)]" mode="client"/> 
     </CLIENTS> 
    </xsl:template> 

    <xsl:template match="CLIENT" mode="client"> 
     <xsl:variable name="name" select="NAME"/> 
     <CLIENT> 
      <NAME> 
       <xsl:value-of select="$name"/> 
      </NAME> 
      <ACCOUNTS> 
       <!--Note that we now have to find the other matching clients *again* - this is the inefficiency that Muenchian grouping eliminates--> 
       <xsl:apply-templates select="/CLIENTS/CLIENT[NAME/text()=$name]" mode="account" /> 
      </ACCOUNTS> 
     </CLIENT> 
    </xsl:template> 

    <xsl:template match="CLIENT" mode="account"> 
     <ACCOUNT> 
      <!--Copy everything else except Name, which is the grouping key --> 
      <xsl:copy-of select="@* | *[not(local-name='NAME')]"/> 
     </ACCOUNT> 
    </xsl:template> 

</xsl:stylesheet> 
+2

+1 для объяснения того, почему Muenchian Grouping даже необходим. – ABach

1

В предыдущий комментарий (под ответом @ Martin), OP спросил, может ли элемент X12T 2.0 for-each-group решить эту проблему.

Когда этот XSLT 2.0 Решение:

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0"> 
    <xsl:output omit-xml-declaration="no" indent="yes" /> 
    <xsl:strip-space elements="*" /> 

    <xsl:template match="/*"> 
    <CLIENTS> 
     <xsl:for-each-group select="CLIENT" group-by="NAME"> 
     <CLIENT> 
      <xsl:sequence select="NAME" /> 
      <xsl:for-each select="current-group()"> 
      <ACCOUNT> 
       <xsl:sequence select="*[not(self::NAME)]" /> 
      </ACCOUNT> 
      </xsl:for-each> 
     </CLIENT> 
     </xsl:for-each-group> 
    </CLIENTS> 
    </xsl:template> 

</xsl:stylesheet> 

... применяется к исходному XML-OP в:

<CLIENTS> 
    <CLIENT> 
    <NAME>John</NAME> 
    <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER> 
    <LAST_USED>2012-10-03</LAST_USED> 
    <AMOUNT>5000</AMOUNT> 
    </CLIENT> 
    <CLIENT> 
    <NAME>John</NAME> 
    <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER> 
    <LAST_USED>2012-10-02</LAST_USED> 
    <AMOUNT>10000</AMOUNT> 
    </CLIENT> 
</CLIENTS> 

... разыскиваемый результат получается:

<?xml version="1.0" encoding="utf-8"?> 
<CLIENTS> 
    <CLIENT> 
    <NAME>John</NAME> 
    <ACCOUNT> 
     <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER> 
     <LAST_USED>2012-10-03</LAST_USED> 
     <AMOUNT>5000</AMOUNT> 
    </ACCOUNT> 
    <ACCOUNT> 
     <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER> 
     <LAST_USED>2012-10-02</LAST_USED> 
     <AMOUNT>10000</AMOUNT> 
    </ACCOUNT> 
    </CLIENT> 
</CLIENTS> 

Объяснение:

Как вы уже догадались, XSLT 2.0 представил элемент for-each-group (и его ассоциированные партнеры, такие как current-group()), чтобы покончить с удивительными/впечатляющими, но потенциально запутанными методологиями группировки, такими как Muenchian Method.

Смежные вопросы