从current-grouping-key()xslt中访问属性

时间:2014-08-30 19:27:49

标签: xml xslt-2.0 xslt-grouping

我有一些包含数千个电影元素的xml,如果导演或作者与另一个有相同的名字,那么它们都有1个或更多的导演和作家元素,如图所示,我添加了@differentiator of birthyear,如果他们不是同一个人

<mediaList>
        <movie id="1603934" dateCreated="2014-08-11">
          <title>Night at the Museum</title>
          <director>Shawn Levy</director>
          <LCSpecialTopics>Comedy</LCSpecialTopics>
          <writer>Robert Ben Garant</writer>
          <writer differentiator="1970">Thomas Lennon</writer>
          <language>English</language>
          <year>2006</year>
       </movie>

      <movie lastModified="2014-08-30" id="1123629" dateCreated="2014-08-04">
          <title type="foreign" xml:lang="cmn">Qiúgǎng Wèishì</title>
          <title>Warriors of Qiugang</title>
          <director>Ruby Yang</director>
          <LCSpecialTopics>Documentary films</LCSpecialTopics>
          <writer differentiator="1951">Thomas Lennon</writer>
          <language>Chinese</language>
          <year>2010</year>
       </movie>
</mediaList>

我当前的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" xmlns:functx="http://www.functx.com" version="2.0">
    <xsl:output method="html" indent="yes"/>

    <xsl:function name="functx:substring-after-last-match" as="xs:string"
        xmlns:functx="http://www.functx.com">
        <xsl:param name="arg" as="xs:string?"/>
        <xsl:param name="regex" as="xs:string"/>

        <xsl:sequence select="
            replace($arg,concat('^.*',$regex),'')
            "/>

    </xsl:function>

    <xsl:template match="mediaList">
        <html>
            <head>
                <link rel="stylesheet" type="text/css" href="CssJs/tableMedia.css"/>
                <title>Media Table</title>
            </head>
            <body>
                <table>
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>Person</th>
                            <th>Movies</th>
                        </tr>
                    </thead>
                    <tbody>
                        <xsl:for-each-group select="movie" group-by="writer | director">
                            <xsl:sort
                                select="functx:substring-after-last-match(current-grouping-key(),'\s')"/>
                            <xsl:sort select="current-grouping-key()"/>
                            <xsl:variable name="person" select="current-grouping-key()"/>

                            <tr>
                                <td>
                                    <xsl:value-of select="position()"/>
                                </td>
                                <td>
                                    <p>
                                        <a id="{current-grouping-key()}">
                                            <xsl:value-of select="$person"/>
                                        </a>
                                    </p>
                                </td>
                                <td>
                                    <xsl:if test="current-group()[writer = $person]">
                                        <h3>Writer</h3>
                                    </xsl:if>
                                    <xsl:apply-templates
                                        select="current-group()[writer = $person]/title[not(@type)]"/>
                                    <xsl:if test="current-group()[director = $person]">
                                        <h3>Director</h3>
                                    </xsl:if>
                                    <xsl:apply-templates
                                        select="current-group()[director = $person]/title[not(@type)]"
                                    />
                                </td>
                            </tr>
                        </xsl:for-each-group>
                    </tbody>
                </table>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="title">
        <p>
            <xsl:value-of select="."/>
            <xsl:apply-templates select="../title[@type]"/>
        </p>
    </xsl:template>

    <xsl:template match="title[@type]">
        <span>
            <xsl:value-of select="concat(' (',.,')')"/>
        </span>
    </xsl:template>            
</xsl:stylesheet>

问题是我在分组后无法访问@differentiator,我收到错误“'/'的第一个操作数的必需项类型是node();提供的值有项类型xs:anyAtomicType只有当提供的值是空序列时,表达式才能成功。“

从阅读类似的问题,我认为必须有一种方法来执行这个组 - 通过使用concat(),但因为每个电影元素可能有超过1个作家/导演,我不能简单地做

group-bye="concat(writer[@differentiator], writer/@differentiator) | writer | director">

所需的输出(代码段)如下所示:

        <tr>
           <td>Some Number</td>
           <td>
              <p><a id="Thomas Lennon1951">Thomas Lennon (1951)</a></p>
           </td>
           <td>
              <h3>Writer</h3>
              <p>Warriors of Qiugang<span> (Qiúgǎng Wèishì)</span></p>
           </td>
        </tr>
        <tr>
           <td>Some Number</td>
           <td>
              <p><a id="Thomas Lennon1970">Thomas Lennon (1970)</a></p>
           </td>
           <td>
              <h3>Writer</h3>
              <p>Night at the Museum</p>
           </td>
        </tr>

2 个答案:

答案 0 :(得分:1)

阐述马丁的简短回答。将writer和潜在differentiator属性的字符串值连接起来,并将其作为“group-by”值传递给for-each-group

然后,两个变量$person$year再次分离分组键的两个部分。这仅解决了writer元素的问题,而不是director

另外,通常ID不能包含空格。如果您的id属性是“true”ID,则应删除ID值中的任何空格。

另外,使用exclude-prefixes="#all"来防止未使用的命名空间出现在输出HTML中。

<强>样式表

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    exclude-result-prefixes="#all"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:functx="http://www.functx.com" version="2.0">
    <xsl:output method="html" indent="yes"/>

    <xsl:function name="functx:substring-after-last-match" as="xs:string"
        xmlns:functx="http://www.functx.com">
        <xsl:param name="arg" as="xs:string?"/>
        <xsl:param name="regex" as="xs:string"/>

        <xsl:sequence select="
            replace($arg,concat('^.*',$regex),'')
            "/>

    </xsl:function>

    <xsl:template match="mediaList">
        <html>
            <head>
                <link rel="stylesheet" type="text/css" href="CssJs/tableMedia.css"/>
                <title>Media Table</title>
            </head>
            <body>
                <table>
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>Person</th>
                            <th>Movies</th>
                        </tr>
                    </thead>
                    <tbody>
                        <xsl:for-each-group select="movie" group-by="writer/concat(@differentiator, '_', .), director">
                            <xsl:sort
                                select="functx:substring-after-last-match(current-grouping-key(),'\s')"/>
                            <xsl:sort select="current-grouping-key()"/>

                           <xsl:variable name="person" select="if (contains(current-grouping-key(),'_')) then substring-after(current-grouping-key(),'_') else current-grouping-key()"/>
                            <xsl:variable name="year" select="if (contains(current-grouping-key(),'_')) then substring-before(current-grouping-key(),'_') else ''"/>

                            <tr>
                                <td>
                                    <xsl:value-of select="position()"/>
                                </td>
                                <td>
                                    <p>
                                        <a id="{concat(replace($person,' ',''),$year)}">
                                            <xsl:value-of select="if ($year) then concat($person,' (',$year,')') else $person"/>
                                        </a>
                                    </p>
                                </td>
                                <td>
                                    <xsl:if test="current-group()[writer = $person]">
                                        <h3>Writer</h3>
                                    </xsl:if>
                                    <xsl:apply-templates
                                        select="current-group()[writer = $person]/title[not(@type)]"/>
                                    <xsl:if test="current-group()[director = $person]">
                                        <h3>Director</h3>
                                    </xsl:if>
                                    <xsl:apply-templates
                                        select="current-group()[director = $person]/title[not(@type)]"
                                    />
                                </td>
                            </tr>
                        </xsl:for-each-group>
                    </tbody>
                </table>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="title">
        <p>
            <xsl:value-of select="."/>
            <xsl:apply-templates select="../title[@type]"/>
        </p>
    </xsl:template>

    <xsl:template match="title[@type]">
        <span>
            <xsl:value-of select="concat(' (',.,')')"/>
        </span>
    </xsl:template>            
</xsl:stylesheet>

XML输入

我使用了以下输入,其中有多部电影由托马斯·列侬(Thomas Lennon,1970)撰写。

<mediaList>
        <movie id="1603934" dateCreated="2014-08-11">
          <title>Night at the Museum</title>
          <director>Shawn Levy</director>
          <LCSpecialTopics>Comedy</LCSpecialTopics>
          <writer>Robert Ben Garant</writer>
          <writer differentiator="1970">Thomas Lennon</writer>
          <language>English</language>
          <year>2006</year>
       </movie>
        <movie id="1603934" dateCreated="2014-08-11">
          <title>Second movie</title>
          <director>Frodo Baggins</director>
          <LCSpecialTopics>Comedy</LCSpecialTopics>
          <writer differentiator="1970">Thomas Lennon</writer>
          <language>English</language>
          <year>2006</year>
       </movie>

      <movie lastModified="2014-08-30" id="1123629" dateCreated="2014-08-04">
          <title type="foreign" xml:lang="cmn">Qiúgǎng Wèishì</title>
          <title>Warriors of Qiugang</title>
          <director>Ruby Yang</director>
          <LCSpecialTopics>Documentary films</LCSpecialTopics>
          <writer differentiator="1951">Thomas Lennon</writer>
          <language>Chinese</language>
          <year>2010</year>
       </movie>
</mediaList>

XML输出

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" type="text/css" href="CssJs/tableMedia.css">
      <title>Media Table</title>
   </head>
   <body>
      <table>
         <thead>
            <tr>
               <th>#</th>
               <th>Person</th>
               <th>Movies</th>
            </tr>
         </thead>
         <tbody>
            <tr>
               <td>1</td>
               <td>
                  <p><a id="FrodoBaggins">Frodo Baggins</a></p>
               </td>
               <td>
                  <h3>Director</h3>
                  <p>Second movie</p>
               </td>
            </tr>
            <tr>
               <td>2</td>
               <td>
                  <p><a id="RobertBenGarant">Robert Ben Garant</a></p>
               </td>
               <td>
                  <h3>Writer</h3>
                  <p>Night at the Museum</p>
               </td>
            </tr>
            <tr>
               <td>3</td>
               <td>
                  <p><a id="ThomasLennon1951">Thomas Lennon (1951)</a></p>
               </td>
               <td>
                  <h3>Writer</h3>
                  <p>Warriors of Qiugang<span> (Qiúgǎng Wèishì)</span></p>
               </td>
            </tr>
            <tr>
               <td>4</td>
               <td>
                  <p><a id="ThomasLennon1970">Thomas Lennon (1970)</a></p>
               </td>
               <td>
                  <h3>Writer</h3>
                  <p>Night at the Museum</p>
                  <p>Second movie</p>
               </td>
            </tr>
            <tr>
               <td>5</td>
               <td>
                  <p><a id="ShawnLevy">Shawn Levy</a></p>
               </td>
               <td>
                  <h3>Director</h3>
                  <p>Night at the Museum</p>
               </td>
            </tr>
            <tr>
               <td>6</td>
               <td>
                  <p><a id="RubyYang">Ruby Yang</a></p>
               </td>
               <td>
                  <h3>Director</h3>
                  <p>Warriors of Qiugang<span> (Qiúgǎng Wèishì)</span></p>
               </td>
            </tr>
         </tbody>
      </table>
   </body>
</html>

编辑作为对您评论的回复:

  

这个逗号在分组中的作用是什么,而不是|?

这是一个很好的后续问题! |运算符返回两组节点的并集,并删除重复项并按文档顺序排列。它的操作数必须是节点集。但是group-by值的第一部分不是一组节点,它只是一个字符串(concat()函数返回一个字符串)。另一方面,逗号运算符,的操作数可以是任何类型的序列,也可以是xs:string类型的序列。

简而言之,您必须使用,而不是|,因为这些操作符接受的操作数类型。它与分组无关。当所有对分组键有贡献的序列都是节点集时,这两个运算符可以互换使用,除了潜在的排序变化。

答案 1 :(得分:0)

您可以使用group-by="writer/concat(@differentiator, '+', .), director"