我有一些包含数千个电影元素的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>
答案 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"
。