使用XSLT进行多级条件打印

时间:2014-06-13 19:57:36

标签: xml xslt formatting grouping

本周早些时候,我问了一个关于使用XSLT进行条件打印的问题,并从helderdarocha收到了一个非常有用的答案。不幸的是,事实证明我需要的答案比我原先想象的要复杂一些。按照他的指示,并使用Muenchian方法进行控制中断,我总结了一个我需要做的新例子。

这是我的XML:

<?xml version="1.0" encoding="UTF-8"?>
<group>
    <person>
        <datum type='medium'>Cartoon</datum>
        <datum type='firstname'>Fred</datum>
        <datum type='lastname'>Flintstone</datum>
    </person>
    <person>
        <datum type='medium'>Cartoon</datum>
        <datum type='firstname'>Wilma</datum>
        <datum type='lastname'>Flintstone</datum>
    </person>
    <person>
        <datum type='medium'>TV</datum>
        <datum type='firstname'>Luke</datum>
        <datum type='lastname'>Duke</datum>
    </person>
    <person>
        <datum type='medium'>TV</datum>
        <datum type='firstname'>Daisy</datum>
        <datum type='lastname'>Duke</datum>
    </person>
    <person>
        <datum type='medium'>Reality</datum>
        <datum type='firstname'>George</datum>
        <datum type='lastname'>Bush</datum>
    </person>
    <person>
        <datum type='medium'>Reality</datum>
        <datum type='firstname'>Barbara</datum>
        <datum type='lastname'>Bush</datum>
    </person>
</group>

这是我的XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>
    <xsl:key name="medium" match="person" use="datum[@type='medium']"/>
    <xsl:key name="lastname" match="person" use="//person/datum[@type='lastname']"/>

    <xsl:template match="person">
        <tr>
            <td><xsl:value-of select="datum[@type='firstname']"/></td>
        </tr>
    </xsl:template>

    <xsl:template match="group">
        <table border="1">
            <xsl:for-each select="person[count(. | key('medium', datum[@type='medium'])[1]) = 1]/datum[@type='medium']">
                <xsl:sort/>
                <tr bgcolor="#cccccc">
                    <td><xsl:value-of select="."/></td>
                </tr>
                <xsl:for-each select="//person[count(. | key('lastname', //person/datum[@type='lastname'])[1]) = 1]/datum[@type='lastname']">
                    <xsl:sort/>
                    <tr bgcolor="#ccffcc">
                        <td><xsl:value-of select="."/></td>
                    </tr>
                    <xsl:apply-templates select="//person[datum[@type='lastname'] = current()]">
                        <xsl:sort select="datum[@type='firstname']"/>
                    </xsl:apply-templates>
                </xsl:for-each>
            </xsl:for-each>
        </table>
    </xsl:template>
</xsl:stylesheet>

这是我的输出:

enter image description here

但这就是我正在尝试的事情:

enter image description here

而且,我在这里假设我的问题是这一行:<xsl:for-each select="//person[count(. | key('lastname', //person/datum[@type='lastname'])[1]) = 1]/datum[@type='lastname']">

我相信我没有正确格式化xpath,但不幸的是,我对XPath的理解仍然相对较弱......我的假设是我需要移动那个&#39; = 1&#39;检查其他地方,但不知道该怎么做。

与往常一样,任何和所有线索都表示赞赏。

2 个答案:

答案 0 :(得分:3)

通过为&#34;媒体&#34;

定义一个键,您已经正确启动了
<xsl:key name="medium" match="person" use="datum[@type='medium']" />

但对于你的&#34;多层次&#34;关键,你正在分组&#34;姓氏&#34;在每个&#34;媒体&#34;。在XSLT 1.0中,这意味着您需要使用连接键,如此

<xsl:key name="medium-lastname" 
         match="person" 
         use="concat(datum[@type='medium'], '|', datum[@type='lastname'])" />

注意'|'分隔符可以是任何内容,只要它不出现在任何&#34;媒体&#34;或&#34;姓氏&#34;值。

所以,然后你开始通过获得独特的&#34;媒体&#34;像这样的价值

<xsl:apply-templates 
     select="person[generate-id() = generate-id(key('medium', datum[@type='medium'])[1])]" 
     mode="medium" />

我在这里使用generate-id方法,但你可以像你一样使用count方法。注意使用&#34;模式&#34;这里。因为我使用的是 xsl:apply-templates ,所以我最终会得到多个匹配 person 元素的模板,因此模式会区分它们。

用这个&#34;媒体&#34;模板,然后你会得到不同的&#34;姓氏&#34;条目,但使用类似的连接键(应用于当前组中的所有元素,由&#34; medium&#34;键访问):

<xsl:apply-templates 
     select="key('medium', datum[@type='medium'])
            [generate-id() = generate-id(key('medium-lastname', concat(datum[@type='medium'], '|', datum[@type='lastname']))[1])]" 
     mode="lastname" />

最后,在&#34;姓氏&#34;模板,您将再次使用连接键获取组中的所有元素:

<xsl:apply-templates 
     select="key('medium-lastname', concat(datum[@type='medium'], '|', datum[@type='lastname']))" />

试试这个XSLT

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

    <xsl:key name="medium" match="person" use="datum[@type='medium']" />
    <xsl:key name="medium-lastname" match="person" use="concat(datum[@type='medium'], '|', datum[@type='lastname'])" />

    <xsl:template match="group">
        <table>
            <xsl:apply-templates select="person[generate-id() = generate-id(key('medium', datum[@type='medium'])[1])]" mode="medium" />
        </table>
    </xsl:template>

    <xsl:template match="person" mode="medium">
        <tr class="medium">
            <td><xsl:value-of select="datum[@type='medium']" /></td>
        </tr>
        <xsl:apply-templates select="key('medium', datum[@type='medium'])[generate-id() = generate-id(key('medium-lastname', concat(datum[@type='medium'], '|', datum[@type='lastname']))[1])]" mode="lastname" />
    </xsl:template>

    <xsl:template match="person" mode="lastname">
        <tr class="lastname">
            <td><xsl:value-of select="datum[@type='lastname']" /></td>
        </tr>
        <xsl:apply-templates select="key('medium-lastname', concat(datum[@type='medium'], '|', datum[@type='lastname']))" />
    </xsl:template>

    <xsl:template match="person">
        <tr>
            <td><xsl:value-of select="datum[@type='firstname']" /></td>
        </tr> 
    </xsl:template>
</xsl:stylesheet>

应用于此XML

<group>
    <person>
        <datum type="medium">Cartoon</datum>
        <datum type="firstname">Fred</datum>
        <datum type="lastname">Flintstone</datum>
    </person>
    <person>
        <datum type="medium">Cartoon</datum>
        <datum type="firstname">Wilma</datum>
        <datum type="lastname">Flintstone</datum>
    </person>
    <person>
        <datum type="medium">Cartoon</datum>
        <datum type="firstname">Barney</datum>
        <datum type="lastname">Rubble</datum>
    </person>
    <person>
        <datum type="medium">TV</datum>
        <datum type="firstname">Daisy</datum>
        <datum type="lastname">Duke</datum>
    </person>
    <person>
        <datum type="medium">TV</datum>
        <datum type="firstname">George</datum>
        <datum type="lastname">Bush</datum>
    </person>
</group>

然后输出以下内容

<table>
   <tr class="medium">
      <td>Cartoon</td>
   </tr>
   <tr class="lastname">
      <td>Flintstone</td>
   </tr>
   <tr>
      <td>Fred</td>
   </tr>
   <tr>
      <td>Wilma</td>
   </tr>
   <tr class="lastname">
      <td>Rubble</td>
   </tr>
   <tr>
      <td>Barney</td>
   </tr>
   <tr class="medium">
      <td>TV</td>
   </tr>
   <tr class="lastname">
      <td>Duke</td>
   </tr>
   <tr>
      <td>Daisy</td>
   </tr>
   <tr class="lastname">
      <td>Bush</td>
   </tr>
   <tr>
      <td>George</td>
   </tr>
</table>

答案 1 :(得分:2)

这里的关键是使用<xsl:for-each-group>按媒介分组,然后按姓氏分组。以下应该有效。你可以在这里看到它:http://xsltransform.net/eiZQaEM/3

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output indent="yes"/>

    <xsl:template match="/">
        <table border="1">
            <xsl:for-each-group select="/group/person" group-by="datum[@type='medium']">
                <tr bgcolor="#cccccc"><td><xsl:value-of select="current-grouping-key()"/></td></tr>

                <xsl:for-each-group select="current-group()" group-by="datum[@type='lastname']">
                    <tr bgcolor="#ccffcc"><td><xsl:value-of select="current-grouping-key()"/></td></tr>

                <xsl:for-each select="current-group()">
                    <tr><td><xsl:value-of select="./datum[@type='firstname']"/></td></tr>
                </xsl:for-each>

            </xsl:for-each-group>
          </xsl:for-each-group>
        </table>
    </xsl:template>

</xsl:transform>
相关问题