xmlxsltxslt-2.0

Using Identity Transformation & Multiple Predicates in Single Match Statement to Silence Node


I am using an identity transformation and template matching to remap an XML element. I need this element to selectively populate when two predicate conditions are met and I'm attempting to do so within the same match statement. So far I am only able to get one of the predicates to match, I suspect I may be violating the process order of control but cannot find a resolution.

In the below XML I need to change text value inside of the node CdtTrfTxInf/PmtTpInf/CtgyPurp/Cd to "SALA". However, I only want this replacement to occur if both of the following conditions are true:

AND

If both of those conditions are not true then instead I want the entire CtgyPurp node silenced.

I also need to remove the CalculatedField node entirely as it is purely a reference value and does not belong in the output.

<?xml version="1.0" encoding="utf-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
   <CstmrCdtTrfInitn>
     <HeaderInfo>
         <Field1>Yes</Field1>
         <Field2>No</Field2>
         <Field3>No</Field3>
     </HeaderInfo>
   <PmtInf>
         <CdtTrfTxInf>
            <InstrId>Payment1</InstrId>
            <PmtTpInf>
               <SvcLvl>
                  <Cd>SEPA</Cd>
               </SvcLvl>
               <CtgyPurp>
                  <Cd>CASH</Cd>
               </CtgyPurp>
            </PmtTpInf>
            <InstdAmt Ccy="EUR">11111</InstdAmt>
            <CdtrAgt>
               <FinInstnId>
                  <PstlAdr>
                     <Ctry>NL</Ctry>
                  </PstlAdr>
               </FinInstnId>
            </CdtrAgt>
            <CalculatedField name="Payment_Category">AD_HOC</CalculatedField>
         </CdtTrfTxInf>
       <CdtTrfTxInf>
           <InstrId>Payment2</InstrId>
           <PmtTpInf>
               <SvcLvl>
                   <Cd>SEPA</Cd>
               </SvcLvl>
               <CtgyPurp>
                   <Cd>CASH</Cd>
               </CtgyPurp>
           </PmtTpInf>
           <InstdAmt Ccy="EUR">22222</InstdAmt>
           <CdtrAgt>
               <FinInstnId>
                   <PstlAdr>
                       <Ctry>US</Ctry>
                   </PstlAdr>
               </FinInstnId>
           </CdtrAgt>
           <CalculatedField name="Payment_Category">AD_HOC</CalculatedField>
       </CdtTrfTxInf>
       <CdtTrfTxInf>
           <InstrId>Payment3</InstrId>
           <PmtTpInf>
               <SvcLvl>
                   <Cd>SEPA</Cd>
               </SvcLvl>
               <CtgyPurp>
                   <Cd>CASH</Cd>
               </CtgyPurp>
           </PmtTpInf>
           <InstdAmt Ccy="EUR">33333</InstdAmt>
           <CdtrAgt>
               <FinInstnId>
                   <PstlAdr>
                       <Ctry>NL</Ctry>
                   </PstlAdr>
               </FinInstnId>
           </CdtrAgt>
           <CalculatedField name="Payment_Category">Junk</CalculatedField>
       </CdtTrfTxInf>
      </PmtInf>
   </CstmrCdtTrfInitn>
</Document>

Here is the XSL I have attempted so far. It will make the "SALA" replacement at the CtgyPurp/Cd level, but the multiple predicate matching is fails to silence the node for the two predicate statements I built in to the match statement.

<?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"
    xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03" 
    exclude-result-prefixes="xs"
    xpath-default-namespace="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03" 
    version="2.0">    


    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="/Document/CstmrCdtTrfInitn/PmtInf">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
        
    <xsl:template match="CdtTrfTxInf[CdtrAgt/FinInstnId/PstlAdr/Ctry/text()='NL'][CalculatedField[@name='Payment_Category']='AD_HOC']/PmtTpInf/CtgyPurp/Cd/text()">
        <xsl:text>SALA</xsl:text>  
    </xsl:template>
    
    <xsl:template match="CdtTrfTxInf[CalculatedField[@name='Payment_Category']!='AD_HOC'][CdtrAgt/FinInstnId/PstlAdr/Ctry/text()!='NL']/PmtTpInf/CtgyPurp"/>
      
    <xsl:template match="CdtTrfTxInf/CalculatedField"/>

</xsl:stylesheet>

Based on these conditions at each node

  1. Ctry="NL" & Calculated Field = "AD_HOC" - Desired outcome: Change CtgryPurp/Cd to "SALA"
  2. Ctry!="NL" (Ctry="US") & CalculatedField = "AD_HOC" - Desired outcome: Remove entire CtgryPurp node
  3. Ctry="NL" & CalculatedField != "AD_HOC" (CalculatedField = "JUNK") - Desired outcome: Remove entire CtgryPurp node

The desired XML output would look like this:

<?xml version="1.0" encoding="utf-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
   <CstmrCdtTrfInitn>
     <HeaderInfo>
         <Field1>Yes</Field1>
         <Field2>No</Field2>
         <Field3>No</Field3>
     </HeaderInfo>
   <PmtInf>
         <CdtTrfTxInf>
            <InstrId>Payment1</InstrId>
            <PmtTpInf>
               <SvcLvl>
                  <Cd>SEPA</Cd>
               </SvcLvl>
               <CtgyPurp>
                  <Cd>SALA</Cd>
               </CtgyPurp>
            </PmtTpInf>
            <InstdAmt Ccy="EUR">11111</InstdAmt>
            <CdtrAgt>
               <FinInstnId>
                  <PstlAdr>
                     <Ctry>NL</Ctry>
                  </PstlAdr>
               </FinInstnId>
            </CdtrAgt>
         </CdtTrfTxInf>
       <CdtTrfTxInf>
           <InstrId>Payment2</InstrId>
           <PmtTpInf>
               <SvcLvl>
                   <Cd>SEPA</Cd>
               </SvcLvl>
           </PmtTpInf>
           <InstdAmt Ccy="EUR">22222</InstdAmt>
           <CdtrAgt>
               <FinInstnId>
                   <PstlAdr>
                       <Ctry>US</Ctry>
                   </PstlAdr>
               </FinInstnId>
           </CdtrAgt>
       </CdtTrfTxInf>
       <CdtTrfTxInf>
           <InstrId>Payment3</InstrId>
           <PmtTpInf>
               <SvcLvl>
                   <Cd>SEPA</Cd>
               </SvcLvl>
           </PmtTpInf>
           <InstdAmt Ccy="EUR">33333</InstdAmt>
           <CdtrAgt>
               <FinInstnId>
                   <PstlAdr>
                       <Ctry>NL</Ctry>
                   </PstlAdr>
               </FinInstnId>
           </CdtrAgt>
       </CdtTrfTxInf>
      </PmtInf>
   </CstmrCdtTrfInitn>
</Document>

Any suggestions on how to update my predicate matching or replace them with more appropriate techniques would be greatly appreciated.Thanks in advance.


Solution

  • I think your described conditions to remove CtgyPurp would be expressed as

    <xsl:template match="CdtTrfTxInf[not(CdtrAgt/FinInstnId/PstlAdr/Ctry/text()='NL') or not(CalculatedField[@name='Payment_Category']='AD_HOC')]/PmtTpInf/CtgyPurp"/>