Wednesday, October 29, 2014

[WSO2 ESB] Comparing two XMLs and find matching elements using WSO2 ESB 4.8.0

Imagine we are getting a list of xml data from an external service and it is required to match this response against another xml payload and find the matching xml elements inside ESB 4.8.0. For e.g. a user send the below request into ESB.
<Accounts>
    <acctNum>123452</acctNum>
    <acctNum>678912</acctNum>
</Accounts>

Inside ESB we are calling an Account DB service which retrieves all the Account elements inside the database and return us. Assume the BE response with Account details is as follows.

<a123: Accounts xmlns:a123="http://test123.com>

    <a123:Account>
        <a123:AcctNum>123451</a123:
AcctNum>
        <a123:Date>2014-03-26</a123:
Date>
    </a123:Account>
    <a123:Account>
        <a123:AcctNum>123452</a123:
AcctNum>
        <a123:Date>2014-04-20</a123:
Date>
    </a123:Account>
</a123:Accounts>
  
 

   
Once this response comes into ESB it is required to compare the two xmls and retrieve Account elements that matches with account numbers which are in incoming request. We can do this through an iterate mediator inside ESB too, however the way that is explained here is rather simple to be used, specially for a scenario when it is not possible to use Iterate mediator.

1. When the incoming request comes, we can use xpath to get a comma separated String with all account numbers in the request. Then assign it to a Property mediator.

<property name="accountsList" expression="string-join(//Accounts/acctNum, ',')" scope="default" type="STRING"/>
This XPath expression uses string-join() function from Xpath 2.0. Therefore it is required to enable the below property in ESB/repository/conf/synapse.properties file in order to support Xpath 2.0

# Uncomment following to support fallback XPATH 2.0 support with DOM and Saxon
synapse.xpath.dom.failover.
enabled=true   
   
2. Once this property is set we can call the BE and get the Account details response. Then the response payload will be passed to the given XSLT stylesheet using XSLT mediator in ESB.
Also as there is comparison needs to be done here, i am passing the previous 'accountsList' property as a parameter into XSLT stylesheet as well.

<xslt key="MatchingAccounts">
    <property name="AccountsList" expression="$ctx:accountsList"
/>
</xslt>

   
3. The stylesheet is added as a local-entry in the ESB with the name 'MatchingAccounts'. If needed you can add this as a Registry resource too.

<localEntry xmlns="http://ws.apache.org/ns/synapse" key="MatchingAccounts">
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a123="http://test123.com xmlns:fn="http://www.w3.org/2005/xpath-functions" version="2.0">
        <xsl:param name="AccountsList"/>
        <xsl:output method="xml" indent="yes"/>
        <xsl:template match="/">
            <Result xmlns="">
                <xsl:for-each select="//a123:Account">
                    <xsl:if test="matches($AccountsList,
a123:AcctNum)">
                        <xsl:copy-of select="."/>
                    </xsl:if>
                </xsl:for-each>
            </Result>
        </xsl:template>
</xsl:stylesheet>   
</localEntry>

   
4. Inside the style sheet if process through each a123:Account/a123:AcctNum element and checks whether the 'accountsList' String contains that account number. If there is a match that <Account> node is taken as results.

5. For the above two XMLs the final payload that comes outside XSLT mediator will be like this, with one matching Account node.
<Result xmlns="">
    <a123:Account>
        <a123:AcctNum>123452</a123:
AcctNum>
        <a123:Date>2014-04-20</a123:
Date>
    </a123:Account>
</Result>