【问题标题】:Group elements in XSLT even if grouping key is missing即使缺少分组键,也可以在 XSLT 中对元素进行分组
【发布时间】:2019-01-07 00:29:00
【问题描述】:

我有以下 XML:

<cars>
  <car type='toyota'>
   <model>Yaris</model>
   <year>1998</year>
   <company>TOYOTA</company>
  </car>
  <car type='kia'>
   <model>Optima</model>
   <year>2002</year>
   <company>KIA</company>
  </car>
  <car type='kia'>
   <model>CERATO</model>
   <year>2009</year>
   <company>KIA</company>
  </car>
  <car type='bmw'>
   <model>M3</model>
   <year>2016</year>
   <company>BMW</company>
  </car>
  <car type='bmw'>
   <model>X5</model>
   <year>2010</year>
  </car>
  <car type='bmw'>
   <model>335i</model>
   <year>2010</year>
   <company>BMW</company>
  </car>
 </cars>

我想按公司元素对汽车进行分组,并在同一元素上进行排序(alpha,升序)。输出应该是这样的:

BMW: M3, X5, 335i
KIA: Optima, CERATO
TOYOTA: Yaris

问题是 car 元素可能不包含公司节点,在这种情况下,必须使用 car/@type 值将元素添加到正确的组中。如何将@type 属性的值映射到基于公司值的正确组?

【问题讨论】:

  • 向我们展示您现有的分组和排序代码,以便我们修复它,例如使用group-by="(company, upper-case(@type))[1]" 并不难。
  • 并标记或建议您在尝试的代码中使用哪个 XSLT 版本。
  • 在您的示例中,COMPANY 元素是多余的,因为它在出现时总是等于 upper-case(@type),所以为什么不忽略它并在 @type 上分组?
  • 我在示例中遗漏了一个重要部分。公司的价值并不总是与类型的价值相同。此外,type 的值可能不存在于 company 元素之一中,在这种情况下,应该使用 type 的值和相关元素呈现一个新组。
  • 我正在使用 XSLT v2。 0

标签: xml xslt xslt-grouping


【解决方案1】:

XSLT 的一个鲜为人知的特性(至少在 2.0 版中)是 当您创建列表时,例如(xx, yy, zz),这个列表 实际上包含仅现有值。 如果例如xx 值为空,它不会是 结果列表。

所以如果你在它后面写[1],你实际上得到第一个 括号之间的表达式列表中的非空元素

在您的评论中,截至 5.08,您要求提供other 类型的解决方案 应该被视为TOYOTA。 在这种情况下,可以使用if ... then ... else ...if (@type = 'other') then 'TOYOTA' else @type

所以你可以这样写脚本:

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

  <xsl:template match="cars">
    <xsl:for-each-group select="car" group-by="upper-case(
      (company, if (@type = 'other') then 'TOYOTA' else @type)[1])">
      <xsl:sort select="current-grouping-key()"/>
      <xsl:value-of select="current-grouping-key()"/>
      <xsl:text>: </xsl:text>
      <xsl:value-of select="string-join(current-group()/model, ', ')"/>
      <xsl:text>&#xA;</xsl:text>
    </xsl:for-each-group>
  </xsl:template>
</xsl:transform>

如你所见:

  • (company, if (@type = 'other') then 'TOYOTA' else @type) 是源列表(大写的参数),
  • [1] 从已创建的内容中获取第一个元素。

我将呼叫upper-case 移至“外层”, 假设model也可以写成小写。

【讨论】:

  • 感谢您的解决方案。如果我有一辆 type='other' 而没有公司的汽车怎么办。在这种情况下,我希望它显示为例如的一部分。 “TOYOTA”作为默认后备值。这甚至可能吗?
  • 我改变了解决方案,所以它可以如你所愿(type = 'other' 被视为'TOYOTA')。
  • 完美运行!非常感谢。
猜你喜欢
  • 1970-01-01
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多