【问题标题】:XSLT - grouping elements in outputXSLT - 对输出中的元素进行分组
【发布时间】:2018-04-04 19:43:37
【问题描述】:

这是一个“我想要我的蛋糕并吃掉它”的问题,其中有很多变数,但我会尽力保持简洁。

我们的软件产品具有三层区域 [A、B 和 C] 的概念。 我们客户的产品只有一层区域 [X]。 客户的 XML 文件始终包含 2000 个区域,即使只有少数实际分配给设备。 由于与此问题无关的原因,我必须从 XSLT 输出中去除未使用的区域。 此输出用作我们的一个软件模块的配置。

我的 xsl 文件中有这个(摘录):

<xsl:for-each select="zoneX">
  <xsl:if test="/{path}/device[zone=current()/@id]">
    <xsl:call-template name="ZoneTypeA"/>
    <xsl:call-template name="ZoneTypeB"/>
    <xsl:call-template name="ZoneTypeC"/>
  </xsl:if>
</xsl:for-each>

这将生成这样的输出(示例):

  <zoneA id="0" .../>
  <zoneB id="0" .../>
  <zoneC id="0" .../>
  <zoneA id="1" .../>
  <zoneB id="1" .../>
  <zoneC id="1" .../>

因为在这个例子中只有 zoneX[id=0] 和 zoneX[id=1] 被分配给一个设备。

在这一点上我必须强调,我们的软件模块对于这些元素的这种特定顺序绝对没有问题。

我的“强迫症”发挥作用(以及同行的)是我们的示例配置和我们的配置文档总是将区域类型组合在一起。

因此,所需的输出将显示为:

  <zoneA id="0" .../>
  <zoneA id="1" .../>

  <zoneB id="0" .../>
  <zoneB id="1" .../>

  <zoneC id="0" .../>
  <zoneC id="1" .../>

(为清楚起见添加了空行)

我尝试为每种区域类型重复 &lt;for-each&gt;

<xsl:for-each select="zones/zone">
  <xsl:if test="/{long-path}/devices[zone=current()/@id]">
    <xsl:call-template name="ZoneTypeA"/>
  </xsl:if>
</xsl:for-each>

<xsl:for-each select="zones/zone">
  <xsl:if test="/{long-path}/devices[zone=current()/@id]">
    <xsl:call-template name="ZoneTypeB"/>
  </xsl:if>
</xsl:for-each>

<xsl:for-each select="zones/zone">
  <xsl:if test="/{long-path}/devices[zone=current()/@id]">
    <xsl:call-template name="ZoneTypeC"/>
  </xsl:if>
</xsl:for-each>

这为我提供了漂亮的输出,但生成输出所需的时间增加了三倍。

是否有另一种方法,我只需迭代设备一次,然后生成将区域类型组合在一起的输出?

编辑

过于简洁,错过了一个重要的细节。阿波斯。 为每个区域寻找设备需要花费大量时间。请参阅上面示例中的&lt;xsl:if&gt;

真实场景示例: 客户配置中有 5000 多台设备。 大多数区域包括多个设备。 仅使用了(2000 个)zoneX 中的 200 个。

在我的第一个解决方案中,我每个 zoneX 只迭代一次设备。

【问题讨论】:

    标签: xslt


    【解决方案1】:

    一种方法是使用另一个包含xsl:for-each 的命名模板,该模板可以作为区域类型的参数传递...

    <xsl:template name="AllZones">
      <xsl:param name="zoneType" />
      <xsl:for-each select="zones/zone">
        <xsl:if test="/{long-path}/devices[zone=current()/@id]">
          <xsl:choose>
            <xsl:when test="$zoneType='A'"><xsl:call-template name="ZoneTypeA"/></xsl:when>
            <xsl:when test="$zoneType='B'"><xsl:call-template name="ZoneTypeB"/></xsl:when>
            <xsl:when test="$zoneType='C'"><xsl:call-template name="ZoneTypeC"/></xsl:when>
          </xsl:choose>
       </xsl:if>
      </xsl:for-each>
    </xsl:template>
    

    你会调用它三遍,像这样......

    <xsl:call-template name="AllZones">
      <xsl:with-param name="zoneType" select="'A'" />
    </xsl:call-template>
    <xsl:call-template name="AllZones">
      <xsl:with-param name="zoneType" select="'B'" />
    </xsl:call-template>
    <xsl:call-template name="AllZones">
      <xsl:with-param name="zoneType" select="'C'" />
    </xsl:call-template>
    

    三个命名模板 ZoneTypeA、ZoneTypeB 和 ZoneTypeC 中的代码有何不同?如果代码相似,您也许可以将它们组合成一个模板,并将区域类型作为参数传递,从而避免所有单独的xsl:call-templates

    【讨论】:

    • 这不会导致每个 zoneX 调用 &lt;xsl:if&gt; 三次吗?这就是浪费大量时间的地方,特别是如果客户的配置有很多设备(实际示例:200 台设备)
    • 每个 ZoneType 只能调用一次 AllZones
    猜你喜欢
    • 2014-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-24
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多