API Connect

API Connect

Join this online group to communicate across IBM product users and experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only
Expand all | Collapse all

How to change element prefix dynamically using XSLT in DATAPOWER

  • 1.  How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Wed July 13, 2022 11:49 AM
    Hi Team,

    Our requirement is we receiving SOAP request as input, in the "Error rule" we need to construct Error message with the combination of Input request message along with error message.

    Here sample input SOAP is:
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iob="http://www.iob.com">
    <soapenv:Header/>
    <soapenv:Body>
    <iob:varyOn>
    <iob:header_channel>internal</iob:header_channel>
    <iob:header_user>nag</iob:header_user>
    <iob:header_workstation>pronteff</iob:header_workstation>
    <iob:header_transactionDate>07072022</iob:header_transactionDate>
    <iob:header_transactionTime>13:02</iob:header_transactionTime>
    </iob:varyOn>
    </soapenv:Body>
    </soapenv:Envelope>

    required output:
    <soap:Envelope xmlns:iob="http://www.iob.com" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
    <iob:varyOn>
    <iob:header_channel>internal</iob:header_channel>
    <iob:header_user>nag</iob:header_user>
    <iob:header_workstation>pronteff</iob:header_workstation>
    <iob:header_transactionDate>07072022</iob:header_transactionDate>
    <iob:header_transactionTime>13:02</iob:header_transactionTime>

    <iob:replyStatus>ERR</iob:replyStatus>
    <iob:faultCode>ESB0017</iob:faultCode>
    <iob:faultString>Invalid message format</iob:faultString>

    </iob:varyOn>
     </soap:Body>
    </soap:Envelope>

    Here Request element prefix was "iob" some times "ns1", "mob" are comming we need to add those name spaces replayStatus,faultCode, and faultString elements prefix also as shown above dynamically.

    I tried so many codes those are having static value, how can we pass name prefix dynamically for Response message.

    Thanks in Advance.

    ------------------------------
    kandula nagababu
    ------------------------------


  • 2.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Thu July 14, 2022 09:14 AM
    You might want to use local-name() and name() function on the request rule's INPUT context to save the name space(s) that are used for certain nodes in a context variable. You can then use this saved context variable in your response rule to prepend the namespace on your error nodes.


    ------------------------------
    Charlie Sumner
    ------------------------------



  • 3.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Thu July 14, 2022 12:01 PM

    Hi Nagababu,

    You need to define them in you xslt declaration and exclude prefixes as shown below.

    exclude-result-prefixes="iob soapenc xsd soap xsi"

    ex:.

    <xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:iob="http://www.iob.com" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

    extension-element-prefixes="dp" exclude-result-prefixes="iob soapenc xsd soap xsi">


    Thanks & Regards

    Avinash



    ------------------------------
    Avinash Mohan Vundale
    ------------------------------



  • 4.  RE: How to change element prefix dynamically using XSLT in DATAPOWER
    Best Answer

    Posted Thu July 14, 2022 02:30 PM

    Hi Kandula,

    You can get the prefix from the name of the parent element (iob:varyOn).  Try the following

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
      version="1.0">
    
       <xsl:template match="soapenv:Body/*">
         <xsl:copy>
           <xsl:apply-templates select="@* | node()" />
           <xsl:variable name="prefix" select="substring-before(name(), ':')" />
           <xsl:element name="{concat($prefix, ':', 'replyStatus')}" namespace="{namespace-uri(.)}">ERR</xsl:element>
           <xsl:element name="{concat($prefix, ':', 'faultCode')}" namespace="{namespace-uri(.)}">ESB0017</xsl:element>
           <xsl:element name="{concat($prefix, ':', 'faultString')}" namespace="{namespace-uri(.)}">Invalid message format</xsl:element>
         </xsl:copy>
       </xsl:template>
    
        <!-- all other elements, simply copy them -->
        <xsl:template match="*">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()" />
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>


    ------------------------------
    Steve Linn
    Senior Consulting I/T Specialist
    IBM
    ------------------------------



  • 5.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Sun July 17, 2022 06:32 AM
    Hi Steve Linn,

    The code is working fine as per the requirement, Thank you so much.

    ------------------------------
    kandula nagababu
    ------------------------------



  • 6.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Mon July 25, 2022 08:39 AM

    Hi Kandula,

    I received your private message with additional questions, but that does not allow me to respond directly to you as that email's reply address is a do not reply address of this community forum.  Please clarify your question here so I can respond.  The request as shown above appears to be a web service request to a iob:varyOn web service.  The example I provided added the additional information you wanted added to that web service request.  Your subsequent question shows an SOAP message with the root body element <iob:Reply> which I'd assume would be your web service's response to the iob:varyOn request you sent??  Are you wanting to manipulate it as well?  Or is the transformation on the request something that the web service didn't like? Since you only provided an expected response and not an actual response for comparison it is difficult to determine what your requirement is.  I'd also refer you to documentation on xslt identity transformations which the example I provided implements a variation for your specific requirement.  There are plenty of sites you can find in an internet search for "xslt identity transformation".

    Best Regards,
    Steve



    ------------------------------
    Steve Linn
    Senior Consulting I/T Specialist
    IBM
    ------------------------------



  • 7.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Thu July 28, 2022 08:13 AM
      |   view attached
    Hi Steve Linn,

    Please ignore the previous requirement,

    Please check my new requirement below:
    Request:
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iob="http://www.iob.com">
    <soapenv:Header/>
    <soapenv:Body>
    <iob:varyOn>
    <iob:Header>
    <iob:header_channel domain="ext">internal</iob:header_channel>
    <iob:header_user>nag</iob:header_user>
    <iob:header_workstation>pronteff</iob:header_workstation>
    </iob:Header>
    <iob:header_transactionDate>07072022</iob:header_transactionDate>
    <iob:header_transactionTime>13:02</iob:header_transactionTime>
    </iob:varyOn>
    </soapenv:Body>
    </soapenv:Envelope>

    Expected output:

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iob="http://www.iob.com">
    <soapenv:Header/>
    <soapenv:Body>

    <iob:Reply>

    <iob:Header>
    <iob:header_channel domain="ext">internal</iob:header_channel>
    <iob:header_user>nag</iob:header_user>
    <iob:header_workstation>pronteff</iob:header_workstation>

    <iob:replyStatus>ERR</iob:replyStatus>

    </iob:Header>
    <iob:header_transactionDate>07072022</iob:header_transactionDate>
    <iob:header_transactionTime>13:02</iob:header_transactionTime>

    <iob:faultCode>ESB0017</iob:faultCode>
    <iob:faultString>Invalid message format</iob:faultString>

    </iob:Reply>
    </soapenv:Body>
    </soapenv:Envelope>

    Problem: I able to frame the response as expected format except attribute values are not coming with my code as i attached below.

    ------------------------------
    kandula nagababu
    ------------------------------

    Attachment(s)



  • 8.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Fri July 29, 2022 11:07 AM

    Hi Kandula,

    So the new requirement:

    1. Replace iob:varyOn with iob:Reply

    2. Add as a child to iob:Header the element iob:replyStatus

    3. Add as a child to iob:Reply the elements iob:faultCode and iob:faultString as before

    4. Continue to be dynamic as far as the namespace uri prefix

    Please try the following:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
      version="1.0">
      
       <xsl:variable name="globalPrefix" select="substring-before(name(/soapenv:Envelope/soapenv:Body/*[1]), ':')" />
       <xsl:variable name="globalNamespace" select="namespace-uri(/soapenv:Envelope/soapenv:Body/*[1])" />
    
       <xsl:template match="soapenv:Body/*">
         <xsl:element name="{concat($globalPrefix, ':', 'Reply')}" namespace="{namespace-uri(.)}">
           <xsl:apply-templates select="@* | node()" />
           <xsl:element name="{concat($globalPrefix, ':', 'faultCode')}" namespace="{namespace-uri(.)}">ESB0017</xsl:element>
           <xsl:element name="{concat($globalPrefix, ':', 'faultString')}" namespace="{namespace-uri(.)}">Invalid message format</xsl:element>
         </xsl:element>
       </xsl:template>
    
       <xsl:template match="*[local-name() = 'Header' and namespace-uri() = $globalNamespace]">
         <xsl:copy>
           <xsl:apply-templates select="@* | node()" />
           <xsl:element name="{concat($globalPrefix, ':', 'ReplyStatus')}" namespace="{namespace-uri(.)}">ERR</xsl:element>
         </xsl:copy>
       </xsl:template>
    
        <!-- all other elements, simply copy them -->
        <xsl:template match="*">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()" />
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>

    Which produces with your input

    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iob="http://www.iob.com">
      <soapenv:Header />
      <soapenv:Body>
        <iob:Reply>
          <iob:Header>
            <iob:header_channel>extinternal</iob:header_channel>
            <iob:header_user>nag</iob:header_user>
            <iob:header_workstation>pronteff</iob:header_workstation>
            <iob:ReplyStatus>ERR</iob:ReplyStatus>
          </iob:Header>
          <iob:header_transactionDate>07072022</iob:header_transactionDate>
          <iob:header_transactionTime>13:02</iob:header_transactionTime>
          <iob:faultCode>ESB0017</iob:faultCode>
          <iob:faultString>Invalid message format</iob:faultString>
        </iob:Reply>
      </soapenv:Body>
    </soapenv:Envelope>

    Given you want to inject at iob:Header a new element, you need an additional template to match that element, copy the iob:Header element, apply templates to copy all of its current children, and then add the new element.

    Regards,

    Steve



    ------------------------------
    Steve Linn
    Senior Consulting I/T Specialist
    IBM
    ------------------------------



  • 9.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Mon August 01, 2022 10:15 AM
    Hi Steve Linn,

    Your code is not working when we passing attributes in the Request, adding attributes in the response is also my requirement for future reference.

    ------------------------------
    kandula nagababu
    ------------------------------



  • 10.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Mon August 01, 2022 05:21 PM

    Hi Kandula,
    My apologies, missed that, the match on the generic template wasn't handling attributes.  Change it to

        <!-- all other elements, simply copy them -->
        <xsl:template match="@* | *">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()" />
            </xsl:copy>
        </xsl:template>

    As for adding attributes in the response for the future, it is simply a matter of adding a template match for the element you wish to add the attribute, and then in addition to the xsl:copy and xsl:apply templates, to do an xsl:attribute to add the desired attribute to the matched element.  For example, I added before the final template a match for header_user and added an attribute newAttribute="somevalue" with the following:

       <!-- add attribute to iob:header_user element -->
       <xsl:template match="*[local-name() = 'header_user' and namespace-uri() = $globalNamespace]">
         <xsl:copy>
           <xsl:attribute name="newAttribute">somevalue</xsl:attribute>
           <xsl:apply-templates select="@* | node()" />
         </xsl:copy>
       </xsl:template>

    which produced the following with your input

    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iob="http://www.iob.com">
      <soapenv:Header />
      <soapenv:Body>
        <iob:Reply>
          <iob:Header>
            <iob:header_channel domain="ext">internal</iob:header_channel>
            <iob:header_user newAttribute="somevalue">nag</iob:header_user>
            <iob:header_workstation>pronteff</iob:header_workstation>
            <iob:ReplyStatus>ERR</iob:ReplyStatus>
          </iob:Header>
          <iob:header_transactionDate>07072022</iob:header_transactionDate>
          <iob:header_transactionTime>13:02</iob:header_transactionTime>
          <iob:faultCode>ESB0017</iob:faultCode>
          <iob:faultString>Invalid message format</iob:faultString>
        </iob:Reply>
      </soapenv:Body>
    </soapenv:Envelope>

    Ultimately, these are all pretty standard xslt identity transformation techniques that you should be able to find examples with your favorite search engine.

    Regards,
    Steve



    ------------------------------
    Steve Linn
    Senior Consulting I/T Specialist
    IBM
    ------------------------------



  • 11.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Tue August 02, 2022 08:50 AM
    Hi Steve Linn,

    It is working now thank you so much for your support.

    ------------------------------
    kandula nagababu
    ------------------------------



  • 12.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Tue August 02, 2022 10:06 AM
      |   view attached
    Hi Steve Linn,

    Please guide me How to debug the xslt code for this i used message elements to print in logs see below code here still i am not understand why 6th, 7th printed first and how <iob:varyOn> replaced with <iob:Reply> in DataPower.

    Please don't explain how it is replaced, tell me debug process for XSLT through that i can understand it.

    ------------------------------
    kandula nagababu
    ------------------------------

    Attachment(s)

    txt
    for community.txt   1 KB 1 version


  • 13.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Tue August 02, 2022 05:36 PM

    Hi Kandula,
    In xslt, the identity transformations are essentially recursive template calls that build the output from the top down but then inside out.   There is a under to covers apply-templates that is done by the xslt engine to run your transformation starting with the root element.  Given your xslt does not have a match="/", the template that matches for the root element is <xsl:template match="@* | *"> which is why you see your message 6 and 7 first as it is processing the soapenv:Envelope element.  So let's look at that template:

    <xsl:template match="@* | *">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
        <xsl:message dp:priority="error">
          <xsl:text>6th:</xsl:text>
          <xsl:copy-of select="."/>
        </xsl:message>
      </xsl:copy>
      <xsl:message dp:priority="error">
        <xsl:text>7th:</xsl:text>
        <xsl:copy-of select="."/>
      </xsl:message>
    </xsl:template>

    The xsl copy will copy the current element which will create the

    <soapenv:Envelope 

    element.  It then does an apply templates, passing in ALL of its nodes and attributes.

    This same template would match the soapenv:Body which would create the <soap:Body and again, it does an apply template, which this time is matched by a different template, as the <iob:varyOn> element is matching 

    <xsl:template match="soapenv:Body/*">
      <xsl:element name="{concat($globalPrefix, ':', 'Reply')}" namespace="{namespace-uri(.)}">
        <xsl:apply-templates select="@* | node()"/>
        <xsl:message dp:priority="error">
          <xsl:text>3rd:</xsl:text>
          <xsl:copy-of select="."/>
        </xsl:message>
        <xsl:element name="{concat($globalPrefix, ':', 'faultCode')}" namespace="{namespace-uri(.)}">ESB0017</xsl:element>
        <xsl:element name="{concat($globalPrefix, ':', 'faultString')}" namespace="{namespace-uri(.)}">Invalid message format</xsl:element>
      </xsl:element>
      <xsl:message dp:priority="error">
        <xsl:text>8th:</xsl:text>
        <xsl:copy-of select="."/>
      </xsl:message>
    </xsl:template>

    that template matches all elements that are children of soapEnv:Body, of course there is only one element here, but this time instead of doing a xsl:copy to create the <iob:varyOn> element, it has an xsl:element to create the iob:Reply element instead.  It then does an apply templates to pass all of the children and attributes of iob:varyOn, all of which will match the last template, and when that recursion reaches a point where there are no child attributes or nodes to process, the </xsl:copy closes out that element.  When this apply-template finally returns, the two xsl:elements will add the two additional elements to the iob:Reply.

    There is no "debugger" that I've used.  I generated these examples with eclipse so perhaps there is a debugger there that will allow you to step through the input xml and see which template is matching.  It's all about understanding xslt and recursion that is inherent in the apply-template processing.  I found this on the net years ago which I found useful at the time.  Hope it helps https://lenzconsulting.com/how-xslt-works/

    Regards,

    Steve



    ------------------------------
    Steve Linn
    Senior Consulting I/T Specialist
    IBM
    ------------------------------



  • 14.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Tue August 16, 2022 09:15 AM
      |   view attached
    Hi Steve Linn,

    Now we face a issue with different SOAP Envelop requests like <soapenv:Envelope>, <soap:Envelope>, <SOAP-ENV:Envelope>.

    How to write a code for above any case match scenario.

    Previously written code is working fine for <soapenv:Envelope> Requests, similarly we tried with "and" operator in template match place and we use the separate multiple templates also but those are not working.

    Please Check different Requests in the Attachment.

    ------------------------------
    kandula nagababu
    ------------------------------

    Attachment(s)

    txt
    different requests.txt   1 KB 1 version


  • 15.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Tue August 16, 2022 10:06 AM

    Hi Kandula,
    In xslt, the actual prefix isn't used on the XPath, but the prefix's associated namespace uri.  In the examples I've provided, you'll see 

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
      version="1.0">

    The XML examples just happen to use the same namespace prefix

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iob="http://www.iob.com">

    but when an XPath is specified in your xslt statements, for example

       <xsl:variable name="globalPrefix" select="substring-before(name(/soapenv:Envelope/soapenv:Body/*[1]), ':')" />
       <xsl:variable name="globalNamespace" select="namespace-uri(/soapenv:Envelope/soapenv:Body/*[1])" />
    
       <xsl:template match="soapenv:Body/*">

    the XPath isn't looking for an exact match of the namespace prefix, but the namespace uri associated with that prefix.  Thus your input XML could have as you note <soapenv:Envelope>, <soap:Envelope>, <SOAP-ENV:Envelope>, but the XPath will continue to work assuming of course the XML specifies an XML namespace for these elements that matches the namespace uri in your stylesheet,

    http://schemas.xmlsoap.org/soap/envelope/

    Regards,
    Steve



    ------------------------------
    Steve Linn
    Senior Consulting I/T Specialist
    IBM
    ------------------------------



  • 16.  RE: How to change element prefix dynamically using XSLT in DATAPOWER

    Posted Tue August 02, 2022 01:06 AM
    might be you're missing local-name() and name() function on the request rule's INPUT context while declaring.

    ------------------------------
    Shivam Singh
    ------------------------------