来自 XSLT 背景的我有时很难找到与我习惯的某些 XSLT 方法等效的 XQuery。这个问题是关于如何按文档顺序对组进行排序,XSLT 2/3 似乎是自动执行的,而使用 XQuery 的顺序似乎是未定义的。
更详细地说:在 XSLT 3(或 2)中进行分组我习惯于(https://www.w3.org/TR/xslt-30/#order-of-groups)以“顺序”返回组的第一次出现”,也就是说,例如,当我对一个元素的一些子元素进行分组时,组的顺序由每个组中第一项的文档顺序决定。在 XQuery 中似乎并非如此,其中https://www.w3.org/TR/xquery-31/#id-group-by说:“元组出现在后分组元组中的顺序流是依赖于实现的。”。
简而言之,问题是:如果
for $city in city
group by $country := $city/@country
return ...
不保证我在 XQuery 中的任何顺序,正在使用
for $city at $pos in city
group by $country := $city/@country
order by $pos[1]
return ...
而是在 XQuery 中拥有“首次出现的顺序”的正确方法,就像 XSLT 默认情况下那样?
背景:在一个简单的示例中(取自 XSLT 3 规范https://www.w3.org/TR/xslt-30/#grouping-examples)中的city
元素
<cities>
<city name="Milano" country="Italia" pop="5"/>
<city name="Paris" country="France" pop="7"/>
<city name="München" country="Deutschland" pop="4"/>
<city name="Lyon" country="France" pop="2"/>
<city name="Venezia" country="Italia" pop="1"/>
</cities>
必须按@country
属性值分组以总结@pop
每个国家的人口值。
一种紧凑的 XSLT 3 方法是
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
expand-text="yes"
version="3.0">
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="cities">
<table>
<thead>
<tr>
<th>Country</th>
<th>Cities</th>
<th>Population</th>
</tr>
</thead>
<tbody>
<xsl:for-each-group select="city" group-by="@country">
<tr>
<td>{ current-grouping-key() }</td>
<td>
<xsl:value-of select="sort(current-group()/@name)" separator=", "/>
</td>
<td>{ sum(current-group()/@pop) }</td>
</tr>
</xsl:for-each-group>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
而且我认为通过兼容的 XSLT 3 实现,我可以保证从输入中获得按顺序 ( Italia
, France
, Deutschland
) 的结果:
<table>
<thead>
<tr>
<th>Country</th>
<th>Cities</th>
<th>Population</th>
</tr>
</thead>
<tbody>
<tr>
<td>Italia</td>
<td>Milano, Venezia</td>
<td>6</td>
</tr>
<tr>
<td>France</td>
<td>Lyon, Paris</td>
<td>9</td>
</tr>
<tr>
<td>Deutschland</td>
<td>München</td>
<td>4</td>
</tr>
</tbody>
</table>
使用 XQuery 但是我认为我不能保证任何特定的顺序,
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:method 'html';
declare option output:html-version '5';
cities/<table>
<thead>
<tr>
<th>Country</th>
<th>Cities</th>
<th>Population</th>
</tr>
</thead>
<tbody>
{
for $city in city
group by $country := $city/@country
return
<tr>
<td>{ $country }</td>
<td>{ string-join( sort($city/@name/string()), ', ') }</td>
<td>{ sum($city/@pop) }</td>
</tr>
}
</tbody>
</table>
https://xqueryfiddle.liberty-development.net/b4GWV7的撒克逊人给我的结果是
<table>
<thead>
<tr>
<th>Country</th>
<th>Cities</th>
<th>Population</th>
</tr>
</thead>
<tbody>
<tr>
<td>Deutschland</td>
<td>München</td>
<td>4</td>
</tr>
<tr>
<td>Italia</td>
<td>Milano, Venezia</td>
<td>6</td>
</tr>
<tr>
<td>France</td>
<td>Lyon, Paris</td>
<td>9</td>
</tr>
</tbody>
</table>
确保从 XQuery 中的“首次出现的顺序”中获取顺序的正确方法是使用
for $city at $pos in city
group by $country := $city/@country
order by $pos[1]
return
<tr>
<td>{ $country }</td>
<td>{ string-join( sort($city/@name/string()), ', ') }</td>
<td>{ sum($city/@pop) }</td>
</tr>
正确或最简单、最紧凑的方式?
似乎在https://xqueryfiddle.liberty-development.net/b4GWV7/1上完成了这项工作,但我想就 XQuery 是否有不同的方式、更惯用的方式提出意见。