6

Useful functions for XSLT 2.0 in SAP PO

 1 year ago
source link: https://blogs.sap.com/2023/02/06/useful-functions-for-xslt-2.0-in-sap-po/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Useful functions for XSLT 2.0 in SAP PO

Dear SAP PO community,

inspired by the blog post “Useful XSLT mapping functions in SAP XI/PI“, I want to provide some more snippets.
Hint: In general Java function calls are often not required anymore with XSLT 2.0, but makes life easier for some SAP PO capabilites, like DynamicConfiguration or AuditLog-Access.

Prerequisite for xslt 2.0:

You must have implemented the following note in your SAP PO system:
1893110 – Integrate external XSLT transformer in PI Mapping Runtime for using XSLT 2.0
2221350 – Support for Java Extensions in the SaxonHE XSLT Transformer for Java Function calls

Please see a working xslt 2.0, providing many aspects and SAP PO specials.
There is value mapping lookup, Java function call, custom xslt function, writing to MappingTrace, conditional Xpath 2.0, writing to AuditLog (Message Log), runtime details of xslt 2.0, date formatting and calculation without Java.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:atrace="java:com.sap.aii.mapping.api.AbstractTrace"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:fn="urn:my:function" xmlns:xml="http://www.w3.org/XML/1998/namespace"
  xmlns:xslthelper="java:de.ho2.sappo.XsltMappingHelper"
  xmlns:vm="java:com.sap.aii.mapping.value.api.XIVMService" exclude-result-prefixes="vm atrace fn xsd xslthelper">
  <xsl:output method="xml" encoding="UTF-8" version="1.0" indent="yes"/>

  <!-- adding standard input parameters, also see https://help.sap.com/docs/SAP_NETWEAVER_750/0b9668e854374d8fa3fc8ec327ff3693/4bf40f2cc0c33de4e10000000a42189e.html?locale=en-US -->

  <!-- Standard: HashMap of input Parameters -->
  <xsl:param name="inputparam"/>

  <!-- Standard: Runtime Constants -->
  <xsl:param name="MessageId"/>
  <xsl:param name="TimeSent"/>
  <xsl:param name="Interface"/>
  <xsl:param name="SenderParty"/>
  <xsl:param name="SenderService"/>
  <xsl:param name="ReceiverParty"/>
  <xsl:param name="ReceiverService"/>

  <!-- Standard: instance of AbstractTrace for MappingTrace -->
  <xsl:param name="MappingTrace"/>

  <!-- Stylesheet Name -->
  <xsl:variable name="xslName" select="replace(document-uri(document('')),'sapximapping:','')"/>

  <!-- Root of document (nothing special) -->
  <xsl:template match="/">

    <!-- Stylesheet version -->
    <xsl:variable name="xslVersion" select="system-property('xsl:version')"/>
    <!-- Xslt processor name -->
    <xsl:variable name="product-name" select="system-property('xsl:product-name')"/>
    <!-- Xslt processor version -->
    <xsl:variable name="product-version" select="system-property('xsl:product-version')"/>

    <!-- writing runtime infos into MappingTrace -->
    <xsl:sequence select="atrace:addInfo($MappingTrace,concat('XSLT processing stylesheet : ', $xslName,' (version: ',$xslVersion, ') on ', $product-name, ' ', $product-version ))"/>
   
    <!-- set DynamicConfiguration by using a Java extension call -->
    <!-- writing infos to the MappingTrace -->
    <xsl:sequence select="atrace:addInfo($MappingTrace, xslthelper:createDcEntry('https://sap/rest', 'operation', 'operation', $inputparam))"/>
   
    <!-- read DynamicConfiguration by using a Java extension call -->
    <!-- writing result into MappingTrace -->
    <xsl:sequence select="atrace:addInfo($MappingTrace, concat ('Read value from DC: ', xslthelper:getDcEntry('https://sap/rest', 'operation', $inputparam))"/>
   
    <!-- writing to Message Log (AuditLog) -->
    <xsl:sequence select="atrace:addInfo($MappingTrace, xslthelper:writeAuditLogMesage('Hello Audit Log (only written during runtime)', string($MessageId), 'success'))"/>

    <!-- Value-Mapping Lookup -->
    <!-- It's important for function calls to provide variable types with xsd:string or string() function -->
    <!-- the ? will also work for empty sequences -->
    <xsl:variable name="vm-result" as="xsd:string?">
      <xsl:call-template name="vm-lookup">
        <xsl:with-param name="psourceAgency" select="'someSourceAgency'"/>
        <xsl:with-param name="psourceScheme" select="'someSourcheScheme'"/>
        <xsl:with-param name="ptargetAgency" select="'someTargetAgency'"/>
        <xsl:with-param name="ptargetScheme" select="'someTargetScheme'"/>
        <xsl:with-param name="pvalue" select="'someSoruceValue'"/>
        <xsl:with-param name="pfailOnFailure" select="'no'"/>
      </xsl:call-template>
    </xsl:variable>

    <!-- current timestamp in xslt 2.0 method - more details about date functions and formatting can be found on the internet, no Java needed anymore -->
    <!-- No Java calls are required anymore for date stuff and even calculation, only a few examples: -->
    <xsl:sequence select="atrace:addInfo($MappingTrace, concat('Timestamp: ', string( current-dateTime()), '
Line-Feed: &#xa; (hex) or &#10; (dec) 

    Date-Formatting: ', string(format-date(current-date(), '[Y0001]/[M01]/[D01]')), 
    '
and one day later: ', string(format-date(current-date()+xsd:dayTimeDuration('P1D'), '[Y0001]/[M01]/[D01]')) ) )"/>

    <!-- simply copy the source XML document (nothing special) -->
    <xsl:copy-of select="."/>

  </xsl:template>

  <!-- Value Mapping Lookup -->
  <xsl:template name="vm-lookup">
    <!-- some parameters -->
    <!-- by using select, default values can be set if not given -->
    <xsl:param name="psourceAgency" select="//EDI_DC40/SNDLAD" required="no" as="xsd:string" />
    <xsl:param name="ptargetAgency" select="//EDI_DC40/RCVLAD" required="no" as="xsd:string"/>
    <xsl:param name="psourceScheme" select="//EDI_DC40/SNDLAD" required="no" as="xsd:string" />
    <xsl:param name="ptargetScheme" select="//EDI_DC40/RCVLAD" required="no" as="xsd:string"/>
    <xsl:param name="pvalue" required="no" as="xsd:string"/>
    <xsl:param name="pnode" required="no" as="node()" select="."/>
    <xsl:param name="pfailOnFailure" select="'no'" as="xsd:string"/>

    <!-- check if either node or value is filled, if both are empty, the stlysheet will cancel the processing with the error of xslt 2.0 function -->
    <xsl:sequence select="if (string-length(local-name($pnode))= 0 and string-length($pvalue)=0 ) then error ( (), 'Please provide either value or node parameter in vm-lookup' ) else ()"/>

    <!-- Value Handling -->
    <xsl:variable name="value" as="xsd:string">
      <xsl:choose>
        <xsl:when test="string-length($pvalue) = 0">
          <xsl:value-of select="$pnode"></xsl:value-of>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$pvalue"></xsl:value-of>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <!-- Source Scheme -->
    <xsl:variable name="sourceScheme" as="xsd:string">
      <xsl:choose>
        <xsl:when test="string-length($psourceScheme)>0">
          <xsl:value-of select="$psourceScheme"></xsl:value-of>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="concat(fn:getXPath($pnode),'#',$value)" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <!-- Target Scheme -->
    <xsl:variable name="targetScheme" as="xsd:string">
      <xsl:choose>
        <xsl:when test="string-length($ptargetScheme)>0">
          <xsl:value-of select="$ptargetScheme"></xsl:value-of>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="concat(fn:getXPath($pnode),'#',$value)" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <!-- Source Agency -->
    <xsl:variable name="sourceAgency" as="xsd:string">
      <xsl:choose>
        <xsl:when test="$psourceAgency">
          <xsl:value-of select="$psourceAgency"/>
        </xsl:when>
        <xsl:when test="$SenderParty">
          <xsl:value-of select="concat($SenderParty,'|',$SenderService)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$SenderService"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <!-- Target Agency -->
    <xsl:variable name="targetAgency" as="xsd:string">
      <xsl:choose>
        <xsl:when test="$ptargetAgency">
          <xsl:value-of select="$ptargetAgency"/>
        </xsl:when>
        <xsl:when test="$ReceiverParty">
          <xsl:value-of select="concat($ReceiverParty,'|',$ReceiverService)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$ReceiverService" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <!-- Value Mapping Lookup -->
    <xsl:variable name="result">
      <xsl:choose>
        <!-- Check all fields are filled for the lookup -->
        <xsl:when test="string-length($sourceAgency) = 0 or string-length($sourceScheme) = 0 or string-length($value) = 0 or string-length($targetAgency) = 0 or string-length($targetScheme) = 0">
          <xsl:value-of select="error( (), concat('Lookup not possible for: ', $sourceAgency,' | ',$sourceScheme, ' | ',$value,' | ',$targetAgency,' | ',$targetScheme) ) "/>
        </xsl:when>
        <xsl:otherwise>
          <!-- writing to the mapping Trace -->
          <xsl:sequence select="atrace:addInfo($MappingTrace,concat('Executing Lookup for: ', $sourceAgency,' | ',$sourceScheme, ' | ',$value,' | ',$targetAgency,' | ',$targetScheme))"/>
          <!-- Usage of the value mapping lookup Java function -->
          <xsl:value-of select="vm:executeMapping($sourceAgency,$sourceScheme, $value,$targetAgency,$targetScheme)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="string-length($result)=0 and $pfailOnFailure = 'yes'">
        <xsl:value-of select="error( (), concat('Lookup failed for: ', $sourceAgency,' | ',$sourceScheme, ' | ',$value,' | ',$targetAgency,' | ',$targetScheme) ) "/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="atrace:addInfo($MappingTrace,concat('Lookup Result: ', $result, ' for: ', $sourceAgency,' | ',$sourceScheme, ' | ',$value,' | ',$targetAgency,' | ',$targetScheme))"/>
        <xsl:value-of select="$result"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- get a simple Xpath (without index) of a node-->
  <xsl:function name="fn:getXPath">
    <xsl:param name="pNode" as="node()"/>
    <xsl:variable name="firstPath">
      <xsl:value-of select="$pNode/ancestor-or-self::*/local-name()" separator="/"/>
    </xsl:variable>
    <xsl:value-of select="concat('/',$firstPath)"/>
  </xsl:function>

</xsl:stylesheet>

XsltHelper class with my custom functions where you can add your own Java Methods.
It uses AuditLogHelper by Ricardo Viana from here: https://blogs.sap.com/2020/02/09/sap-pi-po-java-mapping-create-attachments-sftp

package de.ho2.sappo;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import com.sap.aii.mapping.api.DynamicConfiguration;
import com.sap.aii.mapping.api.DynamicConfigurationKey;
import com.sap.aii.mapping.api.StreamTransformationConstants;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus;

public class XsltMappingHelper
{
  public static final List<String> severities = Arrays.asList("error", "warning", "success");

  public static String getDcEntry(String ns, String name, Object o)
  {
    @SuppressWarnings("unchecked")
    Map<Object, Object> m = (Map<Object, Object>) o;
    DynamicConfiguration dc = (DynamicConfiguration) m
      .get(StreamTransformationConstants.DYNAMIC_CONFIGURATION);

    DynamicConfigurationKey key = DynamicConfigurationKey.create(ns, name);
    String result = dc.get(key);
    return result;
  }

  public static String createDcEntry(String ns, String name, String value, Object o)
  {
    @SuppressWarnings("unchecked")
    Map<Object, Object> m = (Map<Object, Object>) o;
    DynamicConfiguration dc = (DynamicConfiguration) m
      .get(StreamTransformationConstants.DYNAMIC_CONFIGURATION);

    DynamicConfigurationKey key = DynamicConfigurationKey.create(ns, name);
    dc.put(key, value);
    return "added to DC: " + ns + " | " + name + " | " + value;
  }

  public static String writeAuditLogMesage(String message, String messageId, String severity)
    throws IllegalArgumentException
  {
    if (!severities.contains(severity))
      throw new IllegalArgumentException(
        "severity must match one of the values in the list: warning, error, success.");

    try
    {
      AuditLogStatus status = null;
      switch (severity) {
        case "warning":
          status = AuditLogStatus.WARNING;
          break;
        case "error":
          status = AuditLogStatus.ERROR;
          break;
        case "success":
          status = AuditLogStatus.SUCCESS;
      }
      /*
       * Use AuditLogHelper from here
       * https://blogs.sap.com/2020/02/09/sap-pi-po-java-mapping-create-attachments-sftp
       */
      AuditLogHelper logger = AuditLogHelper.getInstance(messageId);
      logger.log(message, status);
      return "Message written: " + message + " (" + severity + ")";
    }
    catch (Exception e)
    {
      if (e.getMessage().equals("String index out of range: 12"))
        return "Cannot write to AuditLog in OperationMapping Testing. It will only work during runtime";
      return e.getMessage();
    }
  }
}

Conclusion

You find many helpful snippets and answers to common question in XSLT mappings, like Mapping Trace, DynamicConfiguration, AuditLog Access, ValueMapping Lookups etc.

Feedback and questions

Please share your feedback in the comment section.
Hint: Don’t miss other and upcoming SAP PO topics, by following the tag: “SAP Prochess Orchestration”

Blog entries: https://blogs.sap.com/tags/477916618626075516391832082074785/
Q&A posts: https://answers.sap.com/tags/477916618626075516391832082074785


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK