Best way to Mashup content from multiple distinct XML documents?

Question for the practitioners here.

If you wanted to retrieve and then mash-up 3 distinct XML documents within an API Proxy, how would you do it?

I can think of multiple ways.

- write a Java callout to do the work, relying on org.w3c.dom.Document etc

- rely on XSLT and the document() function to retrieve stuff from within XSLT, then use xsl:copy-of

- use nodejs and whatever XML parser is available there to prune-graft elements together.

There are probably others. Which approach would you use and why?

Solved Solved
1 3 378
1 ACCEPTED SOLUTION

Here's one way to do it: using the document() function in xslt. This function loads a document from a URL. So, set the URLs in a prior policy, then pass those locations into the XSLT as parameters, then mash them up like so:

(In this example, the documents in question are FHIR documents. )

<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>

The XSL Policy might look 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>


There is a dummyRequest (Message object) because I don't want the XSL to use what's in the request. I want to only grab some external content. Each of loc1, loc2, loc3 carry a string which is a url that will return an XML document.

This works only when there is no security on the FHIR / XML endpoints.

View solution in original post

3 REPLIES 3

Not applicable

Personally, I would use node.js. This type of activity is prone to numerous iterations and that lends itself to a scripting language. XSLT attempts to address this iterative nature by effectively creating it's own arcane DSL, which I personally find cumbersome .;... Everytime I have to use XSLT I have to relearn how to do even the simplest pieces. Simply doing this in code is more straightforward.... So, again, I'd go with node.js and probably a lib like xml2js which I've used in the past.

/geir

Here's one way to do it: using the document() function in xslt. This function loads a document from a URL. So, set the URLs in a prior policy, then pass those locations into the XSLT as parameters, then mash them up like so:

(In this example, the documents in question are FHIR documents. )

<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>

The XSL Policy might look 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>


There is a dummyRequest (Message object) because I don't want the XSL to use what's in the request. I want to only grab some external content. Each of loc1, loc2, loc3 carry a string which is a url that will return an XML document.

This works only when there is no security on the FHIR / XML endpoints.

Not applicable

I'd also go with Node.js instead of a DSLs. DSLs quickly spin out of control and become a monster that only their creators are able to understand. xml2js is good for data transformation module, it also fixes the issues of transforming XML as objects when the elements are supposed to be arrays. Also, manipulating JavaScript objects is very simple and anyone can understand.

Other alternatives to manipulate XML natively in Node.js is to leverage cheeriojs framework. Cheerio is the implementation of jQuery for the server. jQuery syntax is easy and expressive and many developers are familiar with it.