Is there a way to pass a XSLT resource as a parameter of a XSLT on XSLT Policy Transformation?

Not applicable

Hello,

Is there a way to pass a XSLT resource as a parameter on XSLT Policy Transformation?

My need is to apply a dynamic template to remove some undesired elements from an response.

Public Webservice got as an example: http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl

Follow my assets for someone that may want to help me, the goal is to take control of which element is an array of elements.

By default applying only a transformation XML -> JSON policy i got:

"GetWeatherInformationResponse": {
  "GetWeatherInformationResult": {
   "WeatherDescription": [
        {
          "WeatherID": 1,
          "Description": "Thunder Storms",
          "PictureURL": "http://wsf.cdyne.com/WeatherWS/Images/thunderstorms.gif"
        }, ...

But i expected this:

"WeatherDescription": [
  {
    "WeatherID": 1,
    "Description": "Thunder Storms",
    "PictureURL": "http://wsf.cdyne.com/WeatherWS/Images/thunderstorms.gif"
  }, ...

This is a simple scenario, but i have other more complex scenarios where we can have 3 or more lists of elements.

PS: We already made this with an API Gateway that makes use of Saxon XSLT processor, so i know that this is possible, but i'm in doubt on how to pass a reference to an XSLT Resource as a parameter.

XSLT policy:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<XSL async="false" continueOnError="false" enabled="true" name="SoapResponseRewrite">
    <DisplayName>SoapResponseRewrite</DisplayName>
    <Properties/>
    <Source>response</Source>
    <ResourceURL>xsl://SoapResponseRewrite.xsl</ResourceURL>
    <Parameters ignoreUnresolvedVariables="false">
        <Parameter name="soap.response.template" value="apiproxy/resources/xsl/ListWeathersResponse.xsl"/>
    </Parameters>
</XSL

Main XSLT SoapResponseRewrite.xsl (Rewrite the entire SOAP Response according to an template passed as parameter)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" version="1.0">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
  <xsl:param name="soap.response.template"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="*">
    <xsl:param name="source"/>
    <xsl:variable name="current-lookup-elem" select="current()"/>
    <xsl:for-each select="$source/*[local-name() = local-name($current-lookup-elem)]">
      <xsl:choose>
        <xsl:when test="$current-lookup-elem[processing-instruction('xml-multiple')]">
          <xsl:if test="./*">
            <xsl:processing-instruction name="xml-multiple">
              <xsl:value-of select="local-name(./*)"/>
            </xsl:processing-instruction>
          </xsl:if>
          <xsl:apply-templates select="$current-lookup-elem/*">
            <xsl:with-param name="source" select="current()"/>
          </xsl:apply-templates>
        </xsl:when>
        <xsl:otherwise>
          <xsl:element name="{local-name($current-lookup-elem)}">
            <xsl:apply-templates select="$current-lookup-elem/*">
              <xsl:with-param name="source" select="current()"/>
            </xsl:apply-templates>
            <xsl:copy-of select="text()"/>
          </xsl:element>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="/*">
    <xsl:apply-templates select="document($soap.response.template)/*/soap:Envelope/soap:Body/*/node()">
      <xsl:with-param name="source" select="/*/soap:Body/*"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="/">
    <xsl:element name="response">
      <xsl:apply-templates select="node()"/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

XSLT Template ListWeathersResponse.xsl

<?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">
  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
      <GetWeatherInformationResponse xmlns="http://ws.cdyne.com/WeatherWS/">
        <GetWeatherInformationResult>
          <?xml-multiple?>
          <WeatherDescription>
            <WeatherID/>
            <Description/>
            <PictureURL/>
          </WeatherDescription>
        </GetWeatherInformationResult>
      </GetWeatherInformationResponse>
    </soap:Body>
  </soap:Envelope>
</xsl:stylesheet>
1 4 3,891
4 REPLIES 4

Hi,

I'm not sure I understand all of what you are asking. Especially the part about "specifying a resource as a parameter".... I'm not following that.

But, specific to your scenario of converting from XML to JSON. I think your situaiton is that the input XML looks like this:

  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
      <GetWeatherInformationResponse xmlns="http://ws.cdyne.com/WeatherWS/">
        <GetWeatherInformationResult>
          <WeatherDescription>
            <WeatherID>...</WeatherID>
            <Description>...</Description>
            <PictureURL>...</PictureURL>
          </WeatherDescription>
        </GetWeatherInformationResult>
      </GetWeatherInformationResponse>
    </soap:Body>
  </soap:Envelope>

...and you want to convert it to

"WeatherDescription": [
  {
    "WeatherID": 1,
    "Description": "Thunder Storms",
    "PictureURL": "http://wsf.cdyne.com/WeatherWS/Images/thunderstorms.gif"
  }, ...

In today's XMLToJSON policy, we cannot do that. Instead you get JSON versions of all the SOAP and Response elements. In the near future, you will be able to use a new option in the XMLToJSON policy, like so:

<XMLToJSON ... > 
  ... 
  <Options> 
    ... 
    <!-- this is new --> 
  <StripLevels>3</StripLevels>
  </Options> 
</XMLToJSON> 

... in order to get what you seek. This option tells the converter to strip levels of the XML document, and simply discard them, when converting to JSON. In the case of a SOAP message, StripLevels=3 would strip the Envelope, the Body, and the Response elements. If you also wanted to strip the Result element, you would need StripLevels=4.

Levels in the hierarchy are stripped only if they have a single child element.

There is also a related new option, TreatAsArray. It looks like this:

<XMLToJSON ...>
   ...
  <Options>
    <TreatAsArray>
      <Path>Envelope/Body/GetWeatherInformationResponse/GetWeatherInformationResult/WeatherDescription</Path>
    </TreatAsArray>
  </Options>
</XMLToJSON>


This will tell the XMLToJSON to treat the element identified by the path, as an array. By default, XMLToJSON infers the presence of an array by the number of elements present. IF there are two or more child elements named WeatherDescription, then XMLToJSON infers an array. But in some cases, the document has just one element, but you would still like it to be converted to an array. This option allows you to stipulate that.

The associated tickets for these new features are APIRT-578 and APIRT-1144, respectively.

I'll get back to you on when you will be able to use these new features.

Hi Dino,

thanks for your response, it's good to hear that in near future we'll get more control with XMLToJSON and i guess that this could help me on some scenarios.

Going back to my question what i expect is to rewrite the SOAP Response according to a template and to to that i just need to realize how to pass a reference to another XSLT as a parameter on a XSLT.

This diagram shows my need:

+--------+           +--------+      +--------+
|        |           |        |      |        |
|  SOAP  +---------> |REWRITE +----> | OUTPUT |
|RESPONSE|           |  XSLT  |      |  XML   |
|        |           |        |      |        |
+--------+           +---+----+      +--------+
                         ^
                         |Passed as a parameter
                         |
                     +---+----+
                     |        |
                     |TEMPLATE|
                     |  XSLT  |
                     |        |
                     +--------+

I've done it with Vordel Api Gateway, there i've Saxon as a XSLT processor and we just refers the XSLT location as a parameter and it works as expected.

Do you know how to refer a XSLT Resource? I've tried these ways:

ListWeathersResponse.xsl

XSL://ListWeathersResponse.xsl

apiproxy/resources/xsl/ListWeathersResponse.xsl

Best regards

yes, you need a full URL in that location. Example:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!-- this is a parameter set in the XSL Policy config -->
  <xsl:param name="loc1" select="''"/>

  <!-- the following retrieves the remote document into a variable
       available to the stylesheet -->
  <xsl:variable name="xdoc1" select="document($loc1)"/>

  <!-- now, do what you want with the template document... -->

</xsl:stylesheet>

The policy configuration would look like this:

<XSL name='XSL-1'>
  <Source>myDocument</Source>
  <OutputVariable>response.content</OutputVariable>
  <ResourceURL>xsl://my-transform.xsl</ResourceURL>
  <Parameters ignoreUnresolvedVariables='false'>
    <Parameter name='loc1'>https://example.org/my-template.xsl</Parameter>
  </Parameters>
</XSL>

You can host that XSL locally in Edge... in a nodejs server, or otherwise.

Not applicable

Hi Dino,

I tried the above solution to reference an xml file using parameter but no luck. Here are is code(Apigee version 4.16.01.02):

Here i am trying to work with parameter and not with Source value as an example

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<XSL async="false" continueOnError="false" enabled="true" name="XSL-Transform-1">
    <DisplayName>XSL Transform-1</DisplayName>
    <Properties/>
    <Source>calloutResponse</Source>
    <OutputVariable>response.content</OutputVariable>
    <Parameters ignoreUnresolvedVariables="false">
        
        <Parameter name="one">https://api.com/v1/proxyone</Parameter>
        <Parameter name="two">https://api.com/v1/proxytwo</Parameter>
    </Parameters>
    <ResourceURL>xsl://books.xslt</ResourceURL>
</XSL>


books.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html"/>
    <xsl:param name="one" select="''"/>
    <!-- <xsl:param name="two" select="''"/> -->
    <xsl:template match="/">
        <html>
            <MyFiles>
                <xsl:apply-templates select="document('$one')/node()"></xsl:apply-templates>
            </MyFiles>
        </html>
    </xsl:template>
</xsl:stylesheet>

proxyone output:

<entry-list>
  <entry>
    <day>mon</day>
    <amount>34</amount>
  </entry>
</entry-list>


output :
<html>
   <MyFiles></MyFiles>
</html>

expected :
<html>
	<MyFiles>mon34</MyFiles>
</html>


Please advice.