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 (note the * which is required when using <xsl:template match="@*|node()">) -->
  <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 to the 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 to the MappingTrace -->
    <xsl:sequence select="atrace:addInfo($MappingTrace, 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'))"/>

    <!-- 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: &amp;#xa; (hex) or &amp;#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]')) ) )"/>


    <!-- Value-Mapping Lookup -->
    <!-- It's important for function calls to provide variable types with xsd:string, as alternative for using the string() function -->
    <!-- the ? will also work for empty sequences/results, which is similar to null in Java -->
    <xsl:variable name="vm-result" as="xsd:string?" select="vm:executeMapping('sourceAgency','sourceScheme', 'value','targetAgency','targetScheme')"/>
    <xsl:variable name="shouldTheMappingFail" select="yes|no"/>

    <!-- Cancel processing in case of "error"-->
    <!--xslt 2.0 conditional Xpath and xstl 2.0 error function-->
    <xsl:sequence select="if (string-length($vm-result) = 0 and $shouldTheMappingFail='yes')  then (error(  () , 'Lookup value is empty!')) else () "/>

    <xsl:sequence select="atrace:addInfo($MappingTrace,concat('Lookup Result: ', $vm-result ))"/>

    <!-- simply copy the source XML document
    <xsl:copy-of select="."/>-->
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>

  </xsl:template>

<!--simply copy all and use the custom function to print some comments-->
  <xsl:template match="@*|node()">
  <xsl:if test=". instance of element() ">
      <xsl:comment select="concat('Xpath: ', string( fn:getXPath(.) ) )" />
  </xsl:if>
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </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().startsWith("String index out of range:"))
        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

Sara Sampaio

Sara Sampaio

Author Since: March 10, 2022

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x