Transformation with XSLT

In this section, we extend the ingest booking adapter of section Sending to Database. Our aim is to not only insert into table “booking”, but also in table “visit”, see New Horizons Requirements. Table “visit” has a field “bookingId” that references the “id” field of table “booking”. Furthermore, the “visit” table has a field “seq”. This field should contain the sequence number of the <destination> tag in the incoming booking XML. We want to transform the incoming booking, such that the values intended for each row are grouped together.

The example booking of section New Horizons Requirements should be transformed to the following:

<?xml version="1.0" encoding="UTF-8"?>
<destinations>
  <destination>
    <bookingId>1</bookingId>
    <seq>1</seq>
    <hostId>3</hostId>
    <productId>4</productId>
    <startDate>2018-12-27</startDate>
    <endDate>2019-01-02</endDate>
    <price>400.00</price>
  </destination>
</destinations>

In this example, only <seq>1</seq appears because there is only one destination. If there were multiple destinations, the second <destination> would contain <seq>2</seq>, the third would contain <seq>3</seq>, etc.

This can be done with an XSLT transformation, see https://www.w3schools.com/xml/xsl_intro.asp. The Frank!Framework defines a pipe <XsltPipe> that does XSLT transformations. You will use the <XsltPipe> to extend your adapter to apply the transformation outlined above. Please do the following:

  1. Create file projects/gettingStarted/configurations/NewHorizons/booking2destinations.xsl and give it the following contents:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:template match="/">
        <destinations>
          <xsl:apply-templates select="booking/destination">
            <xsl:with-param name="bookingId" select="booking/@id"></xsl:with-param>
          </xsl:apply-templates>
        </destinations>
      </xsl:template>
      <xsl:template match="destination">
        <xsl:param name="bookingId" />
        <destination>
          <bookingId><xsl:value-of select="$bookingId"/></bookingId>
          <seq><xsl:value-of select="position()"/></seq>
          <hostId><xsl:value-of select="@hostId"/></hostId>
          <productId><xsl:value-of select="@productId"/></productId>
          <startDate><xsl:value-of select="startDate"/></startDate>
          <endDate><xsl:value-of select="endDate"/></endDate>
          <price><xsl:value-of select="price"/></price>
        </destination>
      </xsl:template>
    </xsl:stylesheet>
    
  2. Open file projects/gettingStarted/configurations/NewHorizons/ConfigurationIngestBooking.xml.

  3. Add the <XsltPipe> as shown:

    ...
              <Param name="id" xpathExpression="/booking/@id" />
              <Param name="travelerId" xpathExpression="/booking/travelerId" />
              <Param name="price" xpathExpression="/booking/price" />
              <Param name="fee" xpathExpression="/booking/fee" />
            </FixedQuerySender>
            <Forward name="success" path="getDestinations" />
            <Forward name="failure" path="ServerError" />
          </SenderPipe>
          <XsltPipe
              name="getDestinations"
              styleSheetName="booking2destinations.xsl"
              getInputFromSessionKey="originalMessage">
            <Forward name="success" path="Exit"/>
            <Forward name="failure" path="ServerError"/>
          </XsltPipe>
        </Pipeline>
      </Adapter>
    </Module>
    

The pipe shown above has attribute getInputFromSessionKey="originalMessage". You may remember session keys from section Run and Debug an Adapter. They are name/value pairs that accompany the message flowing through the pipeline. The <XsltPipe> should not use the output of its predecessor with name attribute insertBooking. As said in the previous section, the output of that pipe is an XML coming from the database that expresses the result of the INSERT query. Session key originalMessage points to the original input message of the pipeline, which is what we need.