XSLT 3.0 - 删除和替换节点(使用 Saxon 9.7)

XSLT 3.0 - Removing and Replacing nodes (with Saxon 9.7)

我有如下输入 XML(要求突出显示为注释):

<?xml version="1.0" encoding="utf-8"?>
<Aggregated_Data>

    <References>
        <Reference>
            <Type>Company_Reference_ID</Type>
            <Value>Dest Company</Value>
        </Reference>
        <Reference>
            <Type>ABC_Reference_ID</Type>
            <Value>XYZ</Value>
        </Reference>
    </References>


    <wd:root xmlns:wd="urn:com.workday/bsvc">
        <wd:Calculated_Field_Data xmlns:wd="urn:com.workday/bsvc"
            xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
            <wd:Calculated_Field_Reference_ID>Ref</wd:Calculated_Field_Reference_ID>
            <wd:Name>NameABC</wd:Name>
           
            <wd:Conditional_Expression_Calculated_Field_Data>
                <wd:Conditional_Expression_Calculated_Field_Condition_Data>
                    <wd:Condition_Item_Data>
                        <wd:Order>dd</wd:Order>
                        <wd:And_Or_Reference>
                            <wd:ID wd:type="WID">da4e1c34446c11de98360015c5e6daf6</wd:ID>
                            <wd:ID wd:type="And_Or_Operator_Name">And</wd:ID>
                        </wd:And_Or_Reference> 

                        <!-- Need to remove every wd:ID node except the last one (in this case last one is "<wd:ID wd:type="Company_Reference_ID">ABC</wd:ID>") and replace last one's value from the value from References node with the same type (In this case type = Company_Reference_ID) -->
                        <wd:Filter_Instances_Reference>
                            <wd:ID wd:type="WID">e50d309a6d8540e3b533219cfa2c330b</wd:ID>
                            <wd:ID wd:type="Organization_Reference_ID">ABC</wd:ID>
                            <wd:ID wd:type="Company_Reference_ID">ABC</wd:ID>
                        </wd:Filter_Instances_Reference>                        
                       
                    </wd:Condition_Item_Data>
                </wd:Conditional_Expression_Calculated_Field_Condition_Data>
            </wd:Conditional_Expression_Calculated_Field_Data>
        </wd:Calculated_Field_Data>
        
        <!-- Has multiple <wd:Calculated_Field_Data> nodes -->
    </wd:root>

</Aggregated_Data>

预期输出:

<?xml version="1.0" encoding="utf-8"?>
<Aggregated_Data>

    <References>
        <Reference>
            <Type>Company_Reference_ID</Type>
            <Value>Dest Company</Value>
        </Reference>
        <Reference>
            <Type>ABC_Reference_ID</Type>
            <Value>XYZ</Value>
        </Reference>
    </References>


    <wd:root xmlns:wd="urn:com.workday/bsvc">
        <wd:Calculated_Field_Data xmlns:wd="urn:com.workday/bsvc"
            xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
            <wd:Calculated_Field_Reference_ID>Ref</wd:Calculated_Field_Reference_ID>
            <wd:Name>NameABC</wd:Name>
           
            <wd:Conditional_Expression_Calculated_Field_Data>
                <wd:Conditional_Expression_Calculated_Field_Condition_Data>
                    <wd:Condition_Item_Data>
                        <wd:Order>dd</wd:Order>
                        <wd:And_Or_Reference>
                            <wd:ID wd:type="WID">da4e1c34446c11de98360015c5e6daf6</wd:ID>
                            <wd:ID wd:type="And_Or_Operator_Name">And</wd:ID>
                        </wd:And_Or_Reference> 

                        <!-- Notice that after removing all wd:ID nodes except last, the value of last node was replaced from the value from References node of its type -->
                        <wd:Filter_Instances_Reference>                                
                            <wd:ID wd:type="Company_Reference_ID">Dest Company</wd:ID>
                        </wd:Filter_Instances_Reference>                        
                       
                    </wd:Condition_Item_Data>
                </wd:Conditional_Expression_Calculated_Field_Condition_Data>
            </wd:Conditional_Expression_Calculated_Field_Data>
        </wd:Calculated_Field_Data>
        
        <!-- Has multiple <wd:Calculated_Field_Data> nodes -->
    </wd:root>

</Aggregated_Data>

为此,我创建了以下 XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:wd="urn:com.workday/bsvc"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
    exclude-result-prefixes="#all">
    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*" />
    
    <xsl:mode streamable="yes" on-no-match="shallow-copy" use-accumulators="#all"/>
    
     <xsl:accumulator name="RefType" as="xs:string?" initial-value="()" streamable="yes">
        <xsl:accumulator-rule match="References/Reference/Type/text()"
            select="normalize-space()"/>
    </xsl:accumulator>


    <xsl:accumulator name="RefMap" as="map(xs:string,xs:string)" streamable="yes"
        initial-value="map{}">
        <xsl:accumulator-rule match="References/Reference/Value/text()"
            select="map:put($value, accumulator-before('RefType'), normalize-space())"/>
    </xsl:accumulator>
    
    
    
   
    
    <!-- removes all nodes except last one -->
    <xsl:template match="wd:Filter_Instances_Reference/wd:ID[position() != last()]"/>
    
    <!-- replaces last node with value from accumulator from References node -->
    <xsl:template match="wd:Filter_Instances_Reference/wd:ID[position() = last()]">
        <xsl:variable name="type" select="./@wd:type"/>
        <wd:ID>
            <xsl:attribute name="wd:type"><xsl:value-of select="$type"/></xsl:attribute>
            
            <!-- Accumulator map value goes here -->
            <xsl:value-of select="accumulator-before('RefMap')($type)"/>
        </wd:ID>
    </xsl:template>
    
   
</xsl:stylesheet>

问题: 因为我的系统使用较低版本的 Saxon(我相信是 9.7),所以上面的 XSLT 抛出一个流错误: 模板规则被声明为可流式但它不满足可流式规则:匹配模式不是静止的

我怀疑这是因为我在匹配表达式中使用谓词来识别两个模板中 wd:ID 节点的位置。但是,当我在更高版本 (10.6) 的 Saxon 处理器中执行相同的 XSLT 时,我得到了预期的输出,链接 here.

我只是想知道是否有任何其他方法可以在不使用谓词比较节点位置的情况下实现我的要求,或者是否有任何其他更好的方法可以在较低的 XSLT 3.0 saxon 环境中实现我的要求。感谢您的帮助!

您可以设置一个累加器(假设 xpath-default-namespace="urn:com.workday/bsvc",否则在元素名称前加上 wd

  <xsl:accumulator name="id" as="map(xs:string, xs:string)" initial-value="map{}" stremable="yes">
    <xsl:accumulator-rule match="Filter_Instances_Reference" select="map{}"/>
    <xsl:accumulator-rule match="Filter_Instances_Reference/ID/text()" select="map { ../@wd:type/string() : string() }"/>
  </xsl:accumulator>

然后使用模板

  <xsl:template match="Filter_Instances_Reference">
    <xsl:copy>
      <xsl:apply-templates/>
      <wd:ID wd:type="{accumulator-after('id') => map:keys()}">{accumulator-before('RefMap')(accumulator-after('id') => map:keys())}</wd:ID>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="Filter_Instances_Reference/ID"/>