【问题标题】:I need to sum certain nodes in xslt based on the index and value of sibling nodes我需要根据兄弟节点的索引和值对 xslt 中的某些节点求和
【发布时间】:2016-06-18 16:36:37
【问题描述】:

在下面的 xml 结构中,如果记录类型为“ADD”,我需要使用 xsl 对每个记录类型的成本值求和

<records> 
   ...irrelevant nodes...
   <recordType>NO</recordType>
   <recordType>ADD</recordType>
   <recordType>ADD</recordType>
   ... irrelevant nodes...
   <LdgCost>1</LdgCost>
   <LabCostIn>2</LabCostIn>
   <LabCostOut>3</LabCostOut>
   <LdgCost>4</LdgCost>
   <LabCostIn>5</LabCostIn>
   <LabCostOut>6</LabCostOut>
   <LdgCost>7</LdgCost>
   <LabCostIn>8</LabCostIn>
   <LabCostOut>9</LabCostOut>
   ...irrelevant nodes...
</records>

(记录类型的成本是与记录类型相同索引中的以下元素)。这意味着不需要添加类型为“NO”的第一个位置的recordType,因此它的值

<LdgCost>1</LdgCost>
<LabCostIn>2</LabCostIn>
<LabCostOut>3</LabCostOut>

不需要相加。但是,接下来的两种记录类型是“ADD”,因此我需要对

的值求和
<LdgCost>4</LdgCost>
<LabCostIn>5</LabCostIn>
<LabCostOut>6</LabCostOut>
<LdgCost>7</LdgCost>
<LabCostIn>8</LabCostIn>
<LabCostOut>9</LabCostOut>

并将其设置为我的总数。输出只是一个总元素

<total>39</total>

如果使用父子节点,上述xml结构所代表的逻辑结构如下。

<records> 
   <record>
       <recordType>NO</recordType>
       <LdgCost>1</LdgCost>
       <LabCostIn>2</LabCostIn>
       <LabCostOut>3</LabCostOut>
   </record>
   <record>
       <recordType>ADD</recordType>
       <LdgCost>4</LdgCost>
       <LabCostIn>5</LabCostIn>
       <LabCostOut>6</LabCostOut>
   </record>
   <record>
       <recordType>ADD</recordType>
       <LdgCost>7</LdgCost>
       <LabCostIn>8</LabCostIn>
       <LabCostOut>9</LabCostOut>
   </record>
</records>

但是,我必须根据兄弟元素的索引来建立父子关系,而不是使用这种结构。

我将手动执行此操作的方式如下:

  1. 找到第一个&lt;recordType&gt;元素,检查值是ADD还是NO。由于值是 NO 我跳过这个。

  2. 找到下一个&lt;recordType&gt;元素,检查值是ADD还是NO。由于值是 ADD,所以我需要找到与第二条记录相关的 3 项成本。这些成本元素保证存在并位于与其记录类型对应的索引中。

  3. 找到&lt;LdgCost&gt;[2] 元素值并将其添加到总和中。 (索引是二,因为我们在第二个记录类型上,因为我们跳过了第一个并且它的成本被忽略了)

  4. 找到&lt;LdgCostIn&gt;[2] 元素值并将其添加到总和中。

  5. 找到&lt;LdgCostOut&gt;[2] 元素值并将其添加到总和中。

  6. 现在我们已经将第二条记录中的三个成本加到总和中,我们继续第三个 &lt;recordType&gt; 元素并检查它的值。由于值为 ADD,我们找到与第三条记录相关联的 3 个成本。

  7. 找到&lt;LdgCost&gt;[3] 元素值并将其添加到sum中。(索引为3,因为我们在第三个recordType)

  8. 找到&lt;LdgCostIn&gt;[3] 元素值并将其添加到总和中。

  9. 找到&lt;LdgCostOut&gt;[3] 元素值并将其添加到总和中。

  10. 没有更多的 &lt;recordType&gt; 元素需要处理,所以我们返回 39 的总和。

【问题讨论】:

  • 我不明白你的解释。
  • 每条记录都有一个类型和 3 个与之关联的成本。索引 1 处的记录类型为“NO”类型,因此索引 1 处的三个成本(LdgCost、LabCostIn、LabCostOut)不需要相加。索引 2 处的记录类型是“ADD”类型,因此需要将索引 2 处的三个成本相加。索引 3 处的记录类型和成本也是如此。本质上,我想做类似的事情:对于每个记录类型,如果类型是 ADD,那么将与当前记录类型相同的索引处的所有成本相加
  • 您如何确定“记录”有 3 个成本,而不是 2 个或 5 个?
  • 所以每条记录都有三个成本。 1,2,3 是第一个记录类型“NO”的一部分。 4,5,6 是第二个记录类型“ADD”的一部分。 7,8,9 是第三个记录类型“ADD”的一部分 每个记录类型都有三个成本(LdgCost,LabCostIn,LabCostOut)所以记录节点之后的前三个节点(值 1,2,3)是“孩子” (尽管它们没有父子结构)recordType NO 节点,它们代表第一条记录的成本。但是,节点类型为 NO,因此我们将它们从总和中排除。接下来的两种记录类型是 ADD,因此我们将它们的 3 个成本中的每一个都包括在总和中。
  • 每条记录的成本始终相同。因此,如果 recordType 有两个元素,则会有 6 个成本(每条记录 3 个)。如果有一种记录类型,则将产生 3 种成本。等

标签: xml xslt sum xslt-1.0 xslt-2.0


【解决方案1】:

如果您知道可以使用的元素名称

<xsl:template match="/records">
    <xsl:variable name="positions" select="index-of(recordType, 'ADD')"/>
    <total>
        <xsl:value-of select="sum(LdgCost[position() = $positions]
            | LabCostIn[position() = $positions]
            | LabCostOut[position() = $positions])"/>
    </total>
</xsl:template>

【讨论】:

  • 好答案。投票赞成。
【解决方案2】:

--- 根据澄清进行了编辑---

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/records">
    <xsl:variable name="i" select="index-of(recordType, 'ADD')" />
    <xsl:variable name="costs" select="LabCost | LabCostIn | LabCostOut" />
    <total>
        <xsl:copy-of select="sum($costs[(position() - 1) idiv 3 + 1 = $i])"/>
    </total>
</xsl:template>

</xsl:stylesheet>

【讨论】:

  • 因此,如果这些是我的 xml 中唯一的节点,这将起作用。我简化了这个问题。在这个 xml 中还有其他节点,我可以在其中找到记录和成本。xml 看起来更像一些 xml 元素,然后是 recordType 节点,然后是一些更不相关的节点,然后是每个 recordType 的成本。所以那里还有其他数据,但每个 recordType 元素将有 3 个成本,并且成本将通过相应索引链接到 recordType。
  • 我必须为每种记录类型做更多的事情,获取与其他记录类型相关的记录类型的索引(无论是第一个,第二个等),然后如果它是类型 ADD找到具有该索引的成本元素 (LdgCost, LabCostIn, LabCostOut) 节点并将 加到总和中。
  • 感谢您的耐心等待,因为这也不是很容易解释。
  • 好吧,您需要提供一种方法来确定这些成本。理想情况下,您应该提供逐步说明如何手动执行此操作,从头到尾(不依赖执行说明的人的智慧或常识)。
  • 我不确定您所说的确定成本的方法是什么意思。我已尽我所能在原始问题的底部添加了分步说明。
【解决方案3】:

所需的总和可以表示为单个 XPath 2.0 表达式。下面是两种不同的表达方式:

sum(/*/*[name()=('LdgCost', 'LabCostIn', 'LabCostOut')]
           [for $vPos in ceiling(position() div 3)
             return 
               /*/recordType[$vPos] eq 'ADD']
   )

sum(for $vAddPositions in index-of(/*/recordType, 'ADD')
      return
        /*/*[name()=('LdgCost', 'LabCostIn', 'LabCostOut')]
               [ceiling(position() div 3) = $vAddPositions]
   )

这是一个基于 XSLT 2.0 的验证(转换只是输出计算这两个表达式的结果):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:sequence select=
    "sum(/*/*[name()=('LdgCost', 'LabCostIn', 'LabCostOut')]
               [for $vPos in ceiling(position() div 3)
                 return 
                   /*/recordType[$vPos] eq 'ADD']
        )
    "/>

    =====================

    <xsl:sequence select=
    "sum(for $vAddPositions in index-of(/*/recordType, 'ADD')
          return
            /*/*[name()=('LdgCost', 'LabCostIn', 'LabCostOut')]
                   [ceiling(position() div 3) = $vAddPositions]
         )
    "/>
   </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时

<records> 
   ...irrelevant nodes...
    <recordType>NO</recordType>
    <recordType>ADD</recordType>
    <recordType>ADD</recordType>
   ... irrelevant nodes...
    <LdgCost>1</LdgCost>
    <LabCostIn>2</LabCostIn>
    <LabCostOut>3</LabCostOut>
    <LdgCost>4</LdgCost>
    <LabCostIn>5</LabCostIn>
    <LabCostOut>6</LabCostOut>
    <LdgCost>7</LdgCost>
    <LabCostIn>8</LabCostIn>
    <LabCostOut>9</LabCostOut>
   ...irrelevant nodes...
</records>

产生想要的正确结果

39

=====================

39

【讨论】:

    【解决方案4】:

    这样做可以满足您的需要:

    使用 XSLT 1 和 MSXML

    <xsl:stylesheet version="1.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
        exclude-result-prefixes="msxsl"
        >
      <xsl:output method="xml"/>
      <xsl:template match="/">
          <!-- create a set of node for all the values that need to be added -->
          <xsl:variable name="values">
            <xsl:apply-templates select="records/recordType"/>
          </xsl:variable>
            <!-- at this point $values contains a fragment with groups of
            <LdgCost>value</LdgCost>
            <LabCostIn>value</LabCostIn>
            <LabCostOut>value</LabCostOut>
            -->
            <total>
                <!-- sum up all the values -->
               <xsl:value-of select="sum(msxsl:node-set($values)/*)"/>
            </total>
      </xsl:template>
    
      <xsl:template match="recordType[.='ADD']">
          <!-- the index for the ADD recordType -->
          <xsl:variable name="index" select="position()"/>
          <!-- select costs using index -->
          <xsl:copy-of select="/records/LdgCost[$index]"/>
          <xsl:copy-of select="/records/LabCostIn[$index]"/>
          <xsl:copy-of select="/records/LabCostOut[$index]"/>
      </xsl:template>
    </xsl:stylesheet>
    

    我在这里使用节点集函数,每个 xslt 处理器都会有所不同。

    使用你的数据,它返回一个节点:

    <total>39</total>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多