如何只匹配一次xslt中的节点并将其过滤掉

How to match nodes in xslt only once and filter them out

我有一个包含两种“期刊来源”的文件。下面是一个小示例文件

<File>   
   <Record>
        <employee>935388</employee>
        <journal_source>Procurement_Card_Transaction_Verification</journal_source>
        <amount>26.31</amount>
        <wid>123</wid>
        <concat_values>935388|Procurement_Card_Transaction_Verification|26.31</concat_values>
        <Created_Moment>2020-12-31T20:45:45.415-08:00</Created_Moment>
        <Accounting_Date>2020-12-31-08:00</Accounting_Date>
   </Record>   
   <Record>
      <employee>935388</employee>
      <journal_source>Credit_Card_Transaction_Load</journal_source>
      <amount>-26.31</amount>
      <wid>abc</wid>
      <concat_values>935388|Credit_Card_Transaction_Load|26.31</concat_values>
      <Created_Moment>2020-12-20T20:45:45.415-08:00</Created_Moment>
      <Accounting_Date>2020-12-31-08:00</Accounting_Date>
   </Record>
   <Record>
      <employee>935388</employee>
      <journal_source>Credit_Card_Transaction_Load</journal_source>
      <amount>-26.31</amount>
      <wid>def</wid>
      <concat_values>935388|Credit_Card_Transaction_Load|26.31</concat_values>
      <Created_Moment>2020-12-20T20:45:45.415-08:00</Created_Moment>
      <Accounting_Date>2020-12-31-08:00</Accounting_Date>
   </Record>   
</File>

目标是仅输出具有 journal_source 类型“Credit_Card_Transaction_Load”且没有匹配“Procurement_Card_Transaction_Verification”且也具有“[=27=”的节点]”大于信用卡交易。通过匹配,我的意思是它们对“concat_values”字段具有相同的值,除了一个是 Credit,另一个是 Procurement。

这里棘手的部分是我只能匹配一次采购交易。使用后,即使它们也匹配,我也无法将其用于其他信用卡交易。下面是一个示例,说明之前提供的示例需要输出什么(只对在输出中获取“wid”字段感兴趣):

<File>   
    
        <wid>def</wid>

</File>

我首先想到通过在 foreach 循环中更新映射或变量来跟踪已使用的采购交易。然后我会确保检查该交易之前是否已经用于匹配另一笔信用卡交易。但是,这不起作用,因为变量是不可变的。

我也考虑过探索 XSLT 3 的特性并尝试研究累加器,但我没有取得太大进展。

如有任何帮助,我们将不胜感激!

以下示例将采购交易分组到一个映射中,该映射从将员工编号和绝对金额连接到一系列 Record 元素的字符串键开始,然后将此映射用于信用卡交易的迭代中以查找匹配项,如果存在 none 则输出信用卡交易,对于下一次迭代,匹配项将从所用键的映射值中删除:

<?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"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="#all"
  expand-text="yes">
  
  <xsl:function name="mf:get-key" as="xs:string">
    <xsl:param name="record" as="element(Record)"/>
    <xsl:sequence select="replace($record/concat_values, '\|[^|]+\|', '|')"/>
  </xsl:function>

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/File">
    <xsl:copy>
      <xsl:variable name="procurement-map" as="map(xs:string, element(Record)*)">
        <xsl:map>
          <xsl:for-each-group 
            select="Record[journal_source = 'Procurement_Card_Transaction_Verification']" 
            group-by="mf:get-key(.)">
            <xsl:map-entry key="current-grouping-key()" select="current-group()"/>
          </xsl:for-each-group>
        </xsl:map>
      </xsl:variable>
      <xsl:iterate select="Record[journal_source = 'Credit_Card_Transaction_Load']">
        <xsl:param name="procurement-map" select="$procurement-map"/>
        <xsl:variable name="key" select="mf:get-key(.)"/>
        <xsl:variable name="match" select="$procurement-map($key)[xs:dateTime(Created_Moment) gt xs:dateTime(current()/Created_Moment)][1]"/>
        <xsl:if test="not($match)">
          <xsl:copy>
            <xsl:copy-of select="wid"/>
          </xsl:copy>
        </xsl:if>
        <xsl:next-iteration>
          <xsl:with-param name="procurement-map"
             select="if (not($match)) 
                     then $procurement-map 
                     else map:put($procurement-map, $key,  $procurement-map($key) except $match)"/>
        </xsl:next-iteration>
      </xsl:iterate>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>