2

属性値に一致するセットから最初の XML 要素を選択できるようにしたいと考えています。さらに説明するために、次の XML があるとします。

<events>
    <event date="2013-06-17">
        <person first="Joe" last="Bloggs" />
        <person first="John" last="Smith" />
    </event>
    <event date="2013-01-29">
        <person first="Jane" last="Smith" />
        <person first="John" last="Smith" />
    </event>
    <event date="2012-09-03">
        <person first="Joe" last="Bloggs" />
        <person first="John" last="Doe" />
    </event>
    <event date="2012-04-05">
        <person first="Jane" last="Smith" />
        <person first="John" last="Smith" />
        <person first="John" last="Doe" />
    </event>
<event>

最初と最後の属性で一致する person 要素の一意のセットを選択したい。つまり、結果セットは次のようになります。

<person first="Joe" last="Bloggs" />
<person first="John" last="Doe" />
<person first="Jane" last="Smith" />
<person first="John" last="Smith" />

多くのソリューションがあり、そのすべてがこのテーマのバリエーションです。

<xsl:for-each select="//person">
    <xsl:if test="not( preceding::person[ @first = current()/@first and @last = current()/@last ] )">
        <xsl:apply-templates select="." />
    </xsl:if>
</xsl:for-each>

xsl:ifただし、からのテストを、からの選択の述語として含めることができるはずだと私には思えますxsl:for-each

<xsl:apply-templates select="//person[ not( preceding::person[ @first = current()/@first and @last = current()/@last ] ) ]" />

もちろん、current()関数はこれを好まないでしょうが、単一の XPath ステートメントでこれを行う方法を誰かが知っているかどうか疑問に思っていますか?

4

1 に答える 1

4

for-each-groupwithを使用した XSLT 2.0 のアプローチを次に示しますgroup-by

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:template match="events">
  <xsl:for-each-group select="event/person" group-by="concat(@first, '|', @last)">
    <xsl:copy-of select="."/>
  </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

変形する

<events>
    <event date="2013-06-17">
        <person first="Joe" last="Bloggs" />
        <person first="John" last="Smith" />
    </event>
    <event date="2013-01-29">
        <person first="Jane" last="Smith" />
        <person first="John" last="Smith" />
    </event>
    <event date="2012-09-03">
        <person first="Joe" last="Bloggs" />
        <person first="John" last="Doe" />
    </event>
    <event date="2012-04-05">
        <person first="Jane" last="Smith" />
        <person first="John" last="Smith" />
        <person first="John" last="Doe" />
    </event>
</events>

の中へ

<person first="Joe" last="Bloggs"/>
<person first="John" last="Smith"/>
<person first="Jane" last="Smith"/>
<person first="John" last="Doe"/>

XSLT 1.0 を使用すると、

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:key name="by-name" match="event/person" use="concat(@first, '|', @last)"/>

<xsl:template match="events">
  <xsl:for-each select="event/person[generate-id() = generate-id(key('by-name', concat(@first, '|', @last))[1])]">
    <xsl:copy-of select="."/>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

説明については、 http://www.jenitennison.com/xslt/grouping/muenchian.xmlを参照してください。

Muenchian のグループ化は次のように簡略化できます。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:key name="by-name" match="event/person" use="concat(@first, '|', @last)"/>

<xsl:template match="events">
  <xsl:copy-of select="event/person[generate-id() = generate-id(key('by-name', concat(@first, '|', @last))[1])]"/>
</xsl:template>

</xsl:stylesheet>

もちろん、結果ツリーにコピーする代わりにapply-templates、各グループの最初の項目を次のように変換することもできます。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:key name="by-name" match="event/person" use="concat(@first, '|', @last)"/>

<xsl:template match="events">
  <xsl:apply-templates select="event/person[generate-id() = generate-id(key('by-name', concat(@first, '|', @last))[1])]"/>
</xsl:template>

<xsl:template match="person">
  <foo>...</foo>
</xsl:template>

</xsl:stylesheet>

XSLT 2.0 ではfor-each-group、アイテムを格納する変数を使用する必要があります。

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:template match="events">
  <xsl:variable name="first-person-in-groups" as="element(person)*">
    <xsl:for-each-group select="event/person" group-by="concat(@first, '|', @last)">
      <xsl:copy-of select="."/>
    </xsl:for-each-group>
  </xsl:variable>
  <xsl:apply-templates select="$first-person-in-groups"/>
</xsl:template>

<xsl:template match="person">
  <foo>...</foo>
</xsl:template>

</xsl:stylesheet>

そうすれば、一連のperson要素を使用できますapply-templates

于 2013-06-21T09:43:26.757 に答える