从 current-grouping-key() xslt 中访问属性 [英] Access attribute from within current-grouping-key() xslt

查看:23
本文介绍了从 current-grouping-key() xslt 中访问属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些包含数千个电影元素的 xml,它们都可以有 1 个或多个导演和编剧元素,如果导演或编剧与另一个导演或编剧同名,如这里所示,我添加一个出生年份的 @differentiator,如果他们不是同一个人

I have some xml with thousands of movie elements which can all have 1 or MORE director and writer elements, if a director or writer has the same name as another, as seen here, I add an @differentiator of birthyear, if they are NOT the same person

<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 将其转换为一个很好的可读表

my current XSLT to transform this into a nice readable table of

<?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. 仅当提供的值为空序列时,表达式才能成功."

The problem is that I can't access the @differentiator after the group-by, I receive an error of "Required item type of first operand of '/' is node(); supplied value has item type xs:anyAtomicType. The expression can succeed only if the supplied value is an empty sequence."

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

from reading similar problems, i think there must be a way to perform this IN the group-by using concat() but because each movie element may have MORE than 1 writer/director I can't simply do

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

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

desired output (snippet)would look like:

        <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>

推荐答案

详细说明 Martin 给出的简短回答.将 writer 的字符串值和潜在的 differentiator 属性连接起来,并将其作为group-by"值传递给 for-each-group.

Elaborating on the brief answer given by Martin. Concatenate the string value of writer and a potential differentiator attribute and hand it to for-each-group as the "group-by" value.

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

Then, two variables $person and $year separate the two parts of the grouping key again. This only solves the problem for writer elements, not for director.

顺便说一句,ID 通常不能包含空格.如果您的 id 属性是真实"ID,您应该去除 ID 值中的所有空格.

As an aside note, usually IDs cannot contain whitespace. If your id attribute is a "true" ID, you should strip any whitespace in the ID value.

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

Also, use exclude-prefixes="#all" to prevent unused namespaces from appearing in the output 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) 创作的电影.

I used the following input, with more than one movie written by 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>

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

EDIT As a response to your comment:

那个逗号在 group-by 而不是 | 中有什么作用?

What IS that comma doing though in the group-by rather than a |?

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

That's an excellent follow-up question! The | operator returns the union of two sets of nodes, with duplicates removed and in document order. Its operands must necessarily be sets of nodes. But the first part of your group-by value is not a set of nodes, it's just a string (the concat() function returns a string). On the other hand, the operands of the comma operator , can be any kind of sequences, also ones of type xs:string.

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

In short, you have to use , rather than | because of the kinds of operands those operators accept. It has no bearing on the grouping whatsoever. When all sequences that contribute to the grouping key are sets of nodes, the two operators can be used interchangeably, save for potential changes in ordering.

这篇关于从 current-grouping-key() xslt 中访问属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆