Is it possible to mashup multiple XML documents using an XSL in Apigee Edge?

Is it possible to use multiple variables as a source on the XSL transform policy?

I want to mash up multiple responses using xslt.

Solved Solved
0 4 1,265
1 ACCEPTED SOLUTION

No, it is possible to specify multiple "source" documents.

However, you can "Mash up" multiple XML documents using XSL in Apigee Edge. It won't use multiple "sources" but instead will provide the XML via a different mechanism.

I can think of two similar ways. Both require that you Configure your XSL policy to pass parameters into the XSL sheet.

EITHER:

  1. The params will be URLs, or data that URLs will be directly derived from. The URLs which refer to locations to external XML documents that need to be mashed up. The XSL will load those locations into document instances.

    OR
  2. The params will be strings containing XML. The XSL will instantiate documents (parse the XML) from those strings.

In either case, the XSL sheet can then ... access elements from those instantiated documents.

In case 1, the policy config looks like this:

<XSL name='XSL-Mashup'>
  <DisplayName>XSL-Mashup</DisplayName>
  <Source>dummyRequest</Source>
  <OutputVariable>response.content</OutputVariable>
  <ResourceURL>xsl://mashup.xsl</ResourceURL>
  <Parameters ignoreUnresolvedVariables='true'>
    <Parameter name='loc1' ref='loc1'/>
    <Parameter name='loc2' ref='loc2'/>
    <Parameter name='loc3' ref='loc3'/>
  </Parameters>
</XSL>

by the way: The dummyRequest needs to be an actual message containing XML. It must have an XML content-type. You can contrive it with AssignMessage. The XSL need not DO anything with that dummyMessage. But it needs to be present and valid, otherwise the XSL step will be skipped!

The XSL might look like this:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fhir="http://hl7.org/fhir">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <!-- these are parameters set in the XSL Policy config -->
  <xsl:param name="loc1" select="''"/>
  <xsl:param name="loc2" select="''"/>
  <xsl:param name="loc3" select="''"/>
  <!-- the following retrieves the remote documents into variables
       available to the stylesheet -->
  <xsl:variable name="xdoc1" select="document($loc1)"/>
  <xsl:variable name="xdoc2" select="document($loc2)"/>
  <xsl:variable name="xdoc3" select="document($loc3)"/>
  <xsl:template match="/">
    <!-- Mashup 3 FHIR documents -->
    <!-- for now, we omit the link (self, next) elements -->
    <Bundle xmlns="http://hl7.org/fhir">
      <xsl:copy-of select="$xdoc1/fhir:Bundle/fhir:entry"/>
      <xsl:copy-of select="$xdoc2/fhir:Bundle/fhir:entry"/>
      <xsl:copy-of select="$xdoc3/fhir:Bundle/fhir:entry"/>
    </Bundle>
  </xsl:template>
</xsl:stylesheet>

What's happening there: the select="document($loc1)" will instantiate an XML document object within the scope of the execution of the sheet, and make that doc available as a variable within the XSL. The $loc1 must be a string that represents an HTTP/S resolvable location, and the location itself must be reachable from the message processor. Then, by referring to the respective variables in the XSL, you can do whatever you want with those documents. This example just shows copying elements from each of 3 docs.

This approach seems somewhat limited because your source XML docs must be available via HTTP, and it's not possible to configure security on the endpoint there - no way to pass a token or key.

Case 2 is more general and more interesting. The XSL policy configuration looks similar, basically just different parameter names:

<XSL name='XSL-Mashup'>
  <DisplayName>XSL-Mashup</DisplayName>
  <Source>dummyRequest</Source>
  <OutputVariable>response.content</OutputVariable>
  <ResourceURL>xsl://mashup.xsl</ResourceURL>
  <Parameters ignoreUnresolvedVariables='true'>
    <Parameter name='docstring1' ref='docstring1'/>
    <Parameter name='docstring2' ref='docstring2'/>
    <Parameter name='docstring3' ref='docstring3'/>
  </Parameters>
</XSL>

The variables like "docstring{1,2,3}" must contain XML strings. Where you get the XML, is up to you. One possibility is that you've gotten the XML from a ServiceCallout. Another possibility is that you've gotten it from AccessEntity policy. But it doesn't matter. Of course the names of the variables are not important. If you use ServiceCallout then the variable name might be "calloutResponse.content". The Parameter name IS important; it needs to match what you use in the XSL.

OK, let's look at the XSL.

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fhir="http://hl7.org/fhir"
                xmlns:saxon="http://saxon.sf.net/"
                exclude-result-prefixes="saxon fhir" >
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:param name="docstring1" select="''"/>
  <xsl:param name="docstring2" select="''"/>
  <xsl:param name="docstring3" select="''"/>
  <xsl:variable name="xdoc1" select="saxon:parse($docstring1)"/>
  <xsl:variable name="xdoc2" select="saxon:parse($docstring2)"/>
  <xsl:variable name="xdoc3" select="saxon:parse($docstring3)"/>
  <xsl:template match="/">
    <Bundle xmlns="http://hl7.org/fhir">
      <xsl:copy-of select="$xdoc1/fhir:Bundle/fhir:entry"/>
      <xsl:copy-of select="$xdoc2/fhir:Bundle/fhir:entry"/>
      <xsl:copy-of select="$xdoc3/fhir:Bundle/fhir:entry"/>
    </Bundle>
  </xsl:template>
</xsl:stylesheet>

Here, the XSL uses a Saxon feature that instantiates an XML document by parsing a string, and makes that document available to the XSL via a variable. (Apigee documents that it uses Saxon in the XSL policy, so using this Saxon feature will be allowed.)

By referring to the variables in the XSL, you can do whatever you want with those documents. This example just shows copying elements from each of 3 docs, just like the prior example. You can get much more sophisticated of course.

A working example API Proxy is attached here.

apiproxy-xml-mashup.zip

View solution in original post

4 REPLIES 4

No, it is possible to specify multiple "source" documents.

However, you can "Mash up" multiple XML documents using XSL in Apigee Edge. It won't use multiple "sources" but instead will provide the XML via a different mechanism.

I can think of two similar ways. Both require that you Configure your XSL policy to pass parameters into the XSL sheet.

EITHER:

  1. The params will be URLs, or data that URLs will be directly derived from. The URLs which refer to locations to external XML documents that need to be mashed up. The XSL will load those locations into document instances.

    OR
  2. The params will be strings containing XML. The XSL will instantiate documents (parse the XML) from those strings.

In either case, the XSL sheet can then ... access elements from those instantiated documents.

In case 1, the policy config looks like this:

<XSL name='XSL-Mashup'>
  <DisplayName>XSL-Mashup</DisplayName>
  <Source>dummyRequest</Source>
  <OutputVariable>response.content</OutputVariable>
  <ResourceURL>xsl://mashup.xsl</ResourceURL>
  <Parameters ignoreUnresolvedVariables='true'>
    <Parameter name='loc1' ref='loc1'/>
    <Parameter name='loc2' ref='loc2'/>
    <Parameter name='loc3' ref='loc3'/>
  </Parameters>
</XSL>

by the way: The dummyRequest needs to be an actual message containing XML. It must have an XML content-type. You can contrive it with AssignMessage. The XSL need not DO anything with that dummyMessage. But it needs to be present and valid, otherwise the XSL step will be skipped!

The XSL might look like this:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fhir="http://hl7.org/fhir">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <!-- these are parameters set in the XSL Policy config -->
  <xsl:param name="loc1" select="''"/>
  <xsl:param name="loc2" select="''"/>
  <xsl:param name="loc3" select="''"/>
  <!-- the following retrieves the remote documents into variables
       available to the stylesheet -->
  <xsl:variable name="xdoc1" select="document($loc1)"/>
  <xsl:variable name="xdoc2" select="document($loc2)"/>
  <xsl:variable name="xdoc3" select="document($loc3)"/>
  <xsl:template match="/">
    <!-- Mashup 3 FHIR documents -->
    <!-- for now, we omit the link (self, next) elements -->
    <Bundle xmlns="http://hl7.org/fhir">
      <xsl:copy-of select="$xdoc1/fhir:Bundle/fhir:entry"/>
      <xsl:copy-of select="$xdoc2/fhir:Bundle/fhir:entry"/>
      <xsl:copy-of select="$xdoc3/fhir:Bundle/fhir:entry"/>
    </Bundle>
  </xsl:template>
</xsl:stylesheet>

What's happening there: the select="document($loc1)" will instantiate an XML document object within the scope of the execution of the sheet, and make that doc available as a variable within the XSL. The $loc1 must be a string that represents an HTTP/S resolvable location, and the location itself must be reachable from the message processor. Then, by referring to the respective variables in the XSL, you can do whatever you want with those documents. This example just shows copying elements from each of 3 docs.

This approach seems somewhat limited because your source XML docs must be available via HTTP, and it's not possible to configure security on the endpoint there - no way to pass a token or key.

Case 2 is more general and more interesting. The XSL policy configuration looks similar, basically just different parameter names:

<XSL name='XSL-Mashup'>
  <DisplayName>XSL-Mashup</DisplayName>
  <Source>dummyRequest</Source>
  <OutputVariable>response.content</OutputVariable>
  <ResourceURL>xsl://mashup.xsl</ResourceURL>
  <Parameters ignoreUnresolvedVariables='true'>
    <Parameter name='docstring1' ref='docstring1'/>
    <Parameter name='docstring2' ref='docstring2'/>
    <Parameter name='docstring3' ref='docstring3'/>
  </Parameters>
</XSL>

The variables like "docstring{1,2,3}" must contain XML strings. Where you get the XML, is up to you. One possibility is that you've gotten the XML from a ServiceCallout. Another possibility is that you've gotten it from AccessEntity policy. But it doesn't matter. Of course the names of the variables are not important. If you use ServiceCallout then the variable name might be "calloutResponse.content". The Parameter name IS important; it needs to match what you use in the XSL.

OK, let's look at the XSL.

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fhir="http://hl7.org/fhir"
                xmlns:saxon="http://saxon.sf.net/"
                exclude-result-prefixes="saxon fhir" >
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:param name="docstring1" select="''"/>
  <xsl:param name="docstring2" select="''"/>
  <xsl:param name="docstring3" select="''"/>
  <xsl:variable name="xdoc1" select="saxon:parse($docstring1)"/>
  <xsl:variable name="xdoc2" select="saxon:parse($docstring2)"/>
  <xsl:variable name="xdoc3" select="saxon:parse($docstring3)"/>
  <xsl:template match="/">
    <Bundle xmlns="http://hl7.org/fhir">
      <xsl:copy-of select="$xdoc1/fhir:Bundle/fhir:entry"/>
      <xsl:copy-of select="$xdoc2/fhir:Bundle/fhir:entry"/>
      <xsl:copy-of select="$xdoc3/fhir:Bundle/fhir:entry"/>
    </Bundle>
  </xsl:template>
</xsl:stylesheet>

Here, the XSL uses a Saxon feature that instantiates an XML document by parsing a string, and makes that document available to the XSL via a variable. (Apigee documents that it uses Saxon in the XSL policy, so using this Saxon feature will be allowed.)

By referring to the variables in the XSL, you can do whatever you want with those documents. This example just shows copying elements from each of 3 docs, just like the prior example. You can get much more sophisticated of course.

A working example API Proxy is attached here.

apiproxy-xml-mashup.zip

@Dino: Would it be possible to share any working example for my reference to achieve this.

Yes, Madhu; I've attached a working example API proxy above.

Not applicable
@Dino

Thank you for your help.