根据子节点的值增加节点的位置

Increment the position of a node based on the value of child node

这是我的来源XML

<?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Worker>
        <ID>12345</ID>
        <Amount>0.50</Amount>
        <CurrentPayments>2</CurrentPayments>
    </Worker>
    <Worker>
        <ID>45678</ID>
        <Amount>00.50</Amount>
        <CurrentPayments>5</CurrentPayments>
    </Worker>
    <Worker>
        <ID>12345</ID>
        <Amount>00.20</Amount>
        <CurrentPayments>2</CurrentPayments>
    </Worker>
    <Worker>
        <ID>12345</ID>
        <Amount>00.50</Amount>
        <CurrentPayments>2</CurrentPayments>
    </Worker>    
</Workers>

转换上述文件后,CurrentPayments 的位置值应根据元素 CurrentPayments 在源文件中出现的次数递增。

在这种情况下,ID12345Worker 的第一次出现应该具有 CurrentPayments 值 3(即 CurrentPayments 的值源 xml 是 2 递增 1 - Worker 第一次出现 ID12345 )

第二次出现 ID12345Worker 应具有 CurrentPayments 值 4(即 CurrentPayments 在源 [=90 上的值=] 是 2 递增 2 - Worker 第二次出现 ID12345 )

具有 ID12345Worker 第三次出现的 CurrentPayments 值应为 5(即源 [=90 上 CurrentPayments 的值=] 是 2 递增 3 - Worker 第三次出现 ID12345 )

期望的输出如下。

<?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Worker>
        <ID>12345</ID>
        <Amount>0.50</Amount>
        <CurrentPayments>3</CurrentPayments>
    </Worker>
    <Worker>
        <ID>45678</ID>
        <Amount>00.50</Amount>
        <CurrentPayments>6</CurrentPayments>
    </Worker>
    <Worker>
        <ID>12345</ID>
        <Amount>00.20</Amount>
        <CurrentPayments>4</CurrentPayments>
    </Worker>
    <Worker>
        <ID>12345</ID>
        <Amount>00.50</Amount>
        <CurrentPayments>5</CurrentPayments>
    </Worker>    
</Workers>

我尝试使用 position() ,使用 xpath 轴等将当前节点的值与同一元素的子节点进行比较。 <xsl:value-of select="count(//.[(following::text() = text())])+position()"/>

这是我当前的 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" exclude-result-prefixes="xs" version="3.0">
    <xsl:output method="xml" indent="yes"/>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="CurrentPayments">


        <xsl:for-each select=".">
        <CurrentPayments>
            
            <xsl:value-of select=".+ position()"/>
            
        </CurrentPayments>
        
        <!--<CurrentPayments>
            
            <xsl:value-of select="count(//.[(following::text() = text())])+position()"/>

        </CurrentPayments> -->
        
        </xsl:for-each>

    </xsl:template>

</xsl:stylesheet>

你能告诉我如何让它工作吗?谢谢

下面添加

Martin Honnen 的 XSLT 3.0 解决方案非常适合上述要求。但是,我注意到 CurrentPayments 可能不会对所有 <Worker> 都存在。在这种情况下,源 XML 看起来像这样(<Worker> 的最后一个节点 ID 123458没有 CurrentPayments 并且预期的输出应该有元素 CurrentPayments 创建的值 1

<?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Worker>
        <ID>12345</ID>
        <Amount>0.50</Amount>
        <CurrentPayments>2</CurrentPayments>
    </Worker>
    <Worker>
        <ID>45678</ID>
        <Amount>00.50</Amount>
        <CurrentPayments>5</CurrentPayments>
    </Worker>
    <Worker>
        <ID>12345</ID>
        <Amount>00.20</Amount>
        <CurrentPayments>2</CurrentPayments>
    </Worker>
    <Worker>
        <ID>12345</ID>
        <Amount>00.50</Amount>
        <CurrentPayments>2</CurrentPayments>
    </Worker>
    <Worker>
        <ID>123458</ID>
        <Amount>00.50</Amount>        
    </Worker>        
</Workers>

我添加了一个新模板 <xsl:template match="Worker[not(CurrentPayments)]"> 以创建具有值 1 的元素 CurrentPayments。你能告诉我这是否是我唯一的选择吗?

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    exclude-result-prefixes="#all"
    expand-text="yes">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:accumulator name="worker-count" as="map(xs:integer, xs:integer)" initial-value="map{}">
        <xsl:accumulator-rule match="Worker/ID"
            select="let $id := xs:integer(.)
            return
            if (map:contains($value, $id))
            then map:put($value, $id, $value($id) + 1)
            else map:put($value, $id, 1)"/>
    </xsl:accumulator>
    
    <xsl:mode on-no-match="shallow-copy" use-accumulators="worker-count"/>
    
    <xsl:template match="CurrentPayments">
        <xsl:choose>
        
        <xsl:when test=".!=''">        
        <xsl:copy>{. + accumulator-before('worker-count')(xs:integer(../ID))}</xsl:copy>
        </xsl:when>
            <xsl:otherwise>
                
            </xsl:otherwise>
        </xsl:choose>
        
    </xsl:template>
    
    <xsl:template match="Worker[not(CurrentPayments)]">
        <xsl:copy>
            <xsl:apply-templates/>            
            <CurrentPayments>1</CurrentPayments>
        </xsl:copy>        
    </xsl:template>
    
</xsl:stylesheet>

对于 XSLT 3,我认为这是累加器的任务:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:map="http://www.w3.org/2005/xpath-functions/map"
  exclude-result-prefixes="#all"
  expand-text="yes">
  
  <xsl:accumulator name="worker-count" as="map(xs:integer, xs:integer)" initial-value="map{}">
    <xsl:accumulator-rule match="Worker/ID"
      select="let $id := xs:integer(.)
              return
              if (map:contains($value, $id))
              then map:put($value, $id, $value($id) + 1)
              else map:put($value, $id, 1)"/>
  </xsl:accumulator>

  <xsl:mode on-no-match="shallow-copy" use-accumulators="worker-count"/>
  
  <xsl:template match="CurrentPayments">
    <xsl:copy>{. + accumulator-before('worker-count')(xs:integer(../ID))}</xsl:copy>
  </xsl:template>

</xsl:stylesheet>

在该代码示例中,累加器在映射中存储 WorkerID 加上 Worker 的出现次数,CurrentPayments 的规则然后可以简单地访问累加器。