使用generate-id()和键来摆脱XSLT中的重复

时间:2017-01-26 21:39:51

标签: xslt key

来自以下xml数据:

<lieferungen>
    <artikel id="1111">
    <name>apfel</name>
    <preis stueckpreis="true">8.97</preis>
    <lieferant>Fa. Krause</lieferant>
    </artikel>
    <artikel id="7866">
        <name>Kirschen</name>
        <preis stueckpreis="false">10.45</preis>
        <lieferant>Fa. Helbig</lieferant>
    </artikel>
    <artikel id="3526">
        <name>apfel</name>
        <preis stueckpreis="true">12.67</preis>
        <lieferant>Fa. Liebig</lieferant>
    </artikel>
    <artikel id="7866">
        <name>Kirschen</name>
        <preis stueckpreis="false">17.67</preis>
        <lieferant>Fa. Krause</lieferant>
    </artikel>
    <artikel id="3526">
        <name>apfel</name>
        <preis stueckpreis="true">9.54</preis>
        <lieferant>Fa. Mertes</lieferant>
    </artikel>
    <artikel id="7866">
        <name>Kirschen</name>
        <preis stueckpreis="false">16.45</preis>
        <lieferant>Fa. Hoeller</lieferant>
    </artikel>
    <artikel id="7868">
        <name>Kohl</name>
        <preis stueckpreis="false">3.20</preis>
        <lieferant>Fa. Hoeller</lieferant>
    </artikel>
    <artikel id="7866">
        <name>Kirschen</name>
        <preis stueckpreis="false">12.45</preis>
        <lieferant>Fa. Richard</lieferant>
    </artikel>
    <artikel id="3245">
        <name>Bananen</name>
        <preis stueckpreis="false">15.67</preis>
        <lieferant>Fa. Hoeller</lieferant>
    </artikel>
    <artikel id="6745">
        <name>Kohl</name>
        <preis stueckpreis="false">3.10</preis>
        <lieferant>Fa. Reinhardt</lieferant>
    </artikel>
    <artikel id="7789">
        <name>Ananas</name>
        <preis stueckpreis="true">8.60</preis>
        <lieferant>Fa. Richard</lieferant>
    </artikel>
</lieferungen>

我想通过xslt创建以下输出:

Fa. Helbig supplies: Kirschen

Fa. Liebig supplies: apfel

Fa. Krause supplies: apfel Kirschen

Fa. Mertes supplies: apfel

Fa. Hoeller supplies: Kirschen Kohl Bananen

Fa. Reinhardt supplies: Kohl

Fa. Richard supplies: Kirschen Ananas

为此,我编写了以下xslt代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">


    <xsl:template match="lieferungen">
        <html>
            <head>
                <title>
                    <xsl:text>Lieferanten</xsl:text>
                </title>
            </head>
            <body bgcolor="#ffffff">
                <h1>
                    <xsl:text>Suppliers</xsl:text>
                </h1>
                <hr/>
                <xsl:apply-templates select="//lieferant"></xsl:apply-templates>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="lieferant">
        <p>
            <xsl:value-of select="text()"/>
            <xsl:text> supplies: </xsl:text>
            <xsl:for-each select="//artikel[lieferant/text() = current()/text()]">
                <xsl:value-of select="name/text()"/>
                <xsl:text></xsl:text>
            </xsl:for-each>
        </p>
    </xsl:template>


</xsl:stylesheet> 

这给了我以下输出:

Fa. Krause supplies: apfelKirschen
Fa. Helbig supplies: Kirschen
Fa. Liebig supplies: apfel
Fa. Krause supplies: apfelKirschen
Fa. Mertes supplies: apfel
Fa. Hoeller supplies: KirschenKohlBananen
Fa. Hoeller supplies: KirschenKohlBananen
Fa. Richard supplies: KirschenAnanas
Fa. Hoeller supplies: KirschenKohlBananen
Fa. Reinhardt supplies: Kohl
Fa. Richard supplies: KirschenAnanas

为了摆脱重复,我改变了xslt-code如下(注意标记修改/添加的两行的注释):

<?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="xs"
        version="2.0">

         <!--THE FOLLOWING LINE OF CODE WAS ADDED (CHANGE 1/2): -->
         <xsl:key name="suppliers" match="lieferant" use="lieferant" />   
           <xsl:template match="lieferungen">
            <html>
                <head>
                    <title>
                        <xsl:text>Lieferanten</xsl:text>
                    </title>
                </head>
                <body bgcolor="#ffffff">
                    <h1>
                        <xsl:text>Suppliers</xsl:text>
                    </h1>
                    <hr/>

<!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 2/2):-->                        
<xsl:apply-templates select="//lieferant[generate-id()=generate-id(key('suppliers', lieferant)[1])]"></xsl:apply-templates>

                </body>
            </html>
        </xsl:template>
        <xsl:template match="lieferant">
            <p>
                <xsl:value-of select="text()"/>
                <xsl:text> supplies: </xsl:text>
                <xsl:for-each select="//artikel[lieferant/text() = current()/text()]">
                    <xsl:value-of select="name/text()"/>
                    <xsl:text></xsl:text>
                </xsl:for-each>
            </p>
        </xsl:template>


    </xsl:stylesheet>

然而,这打破了代码。我使用的代码有什么问题?

2 个答案:

答案 0 :(得分:2)

您想要的输出可以通过以下方式实现:

XSLT 2.0

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

<xsl:template match="/lieferungen">
    <html>
        <head>
            <title>Lieferanten</title>
        </head>
        <body>
            <h1>Suppliers</h1>
            <hr/>
            <xsl:for-each-group select="artikel" group-by="lieferant">
                <p>
                    <xsl:value-of select="current-grouping-key()"/>
                    <xsl:text> supplies: </xsl:text>
                    <xsl:value-of select="current-group()/name"/>
                </p>
            </xsl:for-each-group>
        </body>
    </html>
</xsl:template>

</xsl:stylesheet> 

答案 1 :(得分:1)

如果您使用的是样式表版本所示的XSLT 2.0处理器,请参阅michael.hor257k的回答。否则,这是你的样式表出了什么问题...

在您更改代码的两个地方,上下文为lieferant,但您尝试使用子lieferant的值。在您的来源中,lieferant没有孩子lieferant

例如,在您的xsl:键中,您选择lieferant并使用lieferant。如果您要use选择相同的节点,请使用.

使用key()时也是如此;它在lieferant的谓词中使用,因此要使用该上下文使用.

这是您更新的XSLT。我在你所做的相同位置修改了它,但是在xsl:text中添加了一个空格来分隔name。我添加了一条注释,指出此更改以保持一致。

我做的另一件事是将版本更改为1.0并删除未使用的xs命名空间声明。 (这个样式表的输出中没有真正改变任何内容。)

更新了XSLT 1.0

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

  <!--THE FOLLOWING LINE OF CODE WAS ADDED (CHANGE 1/3): -->
  <xsl:key name="suppliers" match="lieferant" use="." />
  <xsl:template match="lieferungen">
    <html>
      <head>
        <title>
          <xsl:text>Lieferanten</xsl:text>
        </title>
      </head>
      <body bgcolor="#ffffff">
        <h1>
          <xsl:text>Suppliers</xsl:text>
        </h1>
        <hr/>

        <!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 2/3):-->
        <xsl:apply-templates
          select="//lieferant[generate-id()=generate-id(key('suppliers', .)[1])]"/>

      </body>
    </html>
  </xsl:template>
  <xsl:template match="lieferant">
    <p>
      <xsl:value-of select="text()"/>
      <xsl:text> supplies: </xsl:text>
      <xsl:for-each select="//artikel[lieferant/text() = current()/text()]">
        <xsl:value-of select="name/text()"/>
        <!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 3/3):-->
        <xsl:text> </xsl:text>
      </xsl:for-each>
    </p>
  </xsl:template>

</xsl:stylesheet>

<强>更新

就像Michael在评论中提到的那样,你使用<xsl:for-each select="//artikel[lieferant/text() = current()/text()]">失去了Muenchian分组的一半好处。

我建议您创建一个在artikel级别选择并使用lieferant作为密钥的密钥。

示例(带有注释以指示所有更改)...

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

  <!--THE FOLLOWING LINE OF CODE WAS ADDED (CHANGE 1/7): -->
  <xsl:key name="suppliers" match="artikel" use="lieferant" />
  <xsl:template match="lieferungen">
    <html>
      <head>
        <title>
          <xsl:text>Lieferanten</xsl:text>
        </title>
      </head>
      <body bgcolor="#ffffff">
        <h1>
          <xsl:text>Suppliers</xsl:text>
        </h1>
        <hr/>

        <!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 2/7):-->
        <xsl:apply-templates
          select="//artikel[generate-id()=generate-id(key('suppliers', lieferant)[1])]"/>

      </body>
    </html>
  </xsl:template>
  <!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 3/7):-->
  <xsl:template match="artikel">
    <p>
      <!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 4/7):-->
      <xsl:value-of select="lieferant"/>
      <xsl:text> supplies: </xsl:text>
      <!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 5/7):-->
      <xsl:for-each select="key('suppliers',lieferant)">
        <!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 6/7):-->
        <xsl:value-of select="name"/>
        <!--THE FOLLOWING LINE OF CODE WAS MODIFIED (CHANGE 7/7):-->
        <xsl:text> </xsl:text>
      </xsl:for-each>
    </p>
  </xsl:template>

</xsl:stylesheet>

但是,你可以修改xsl:for-each:

<xsl:for-each select="key('suppliers',.)">
  <xsl:value-of select="../name"/>
  <xsl:text> </xsl:text>
</xsl:for-each>