Code for this blog can be found on github at
https://github.com/galapagosfinch

Monday, November 28, 2011

Using Groovy as a Transformer with Spring Integration

Working with XML can be "difficult" in a lot of different situations.  But consider the case where your internal system is using JSON for generic data capture, but the external system you need to integrate with is using SOAP?  What could be more convoluted that parsing JSON in order to express XML?  Well, our group found an elegant way to hide all that complexity.


We were in the process of considering a number of open-source Java libraries for manipulating JSON, when Carlos Lawton commented about how nice it is to work with XML in Groovy, and that it would make for a good Transformer.  That was enough prodding for me.

In previous versions of Groovy, the JsonSlurper class was part of an external project.  As of Groovy 1.8, the JSON support is built directly into the language, so we now have the ability (with raw Groovy) to consume JSON and express XML, as well as consume XML and express JSON.  In this blog post, I'll be focusing on the former.

The biggest trick to getting this to work was getting the groovy compiler integrated with maven.  Do not try to use GMaven for Groovy 1.8, as it packages Groovy 1.7 and won't be getting upgraded (GMaven is discontinued).  So, use the Groovy-Eclipse compiler.  If you check out my code, you can find the maven config that worked for me.

Now, the code:

class JsonToFloodRequestXmlTransformer extends AbstractPayloadTransformer<String, String> {
    @Override
    protected String transformPayload(String t) {
        def json = (new JsonSlurper()).parseText(t);
        def writer = new StringWriter();
        def xml = new MarkupBuilder(writer);

        xml.REQUEST_GROUP(MISMOVersionID: "2.4") {
            SUBMITTING_PARTY(LoginAccountIdentifier: "${json.credentials.username}",
                    LoginAccountPassword: "${json.credentials.password}")
            REQUEST(InternalAccountIdentifier: "${json.credentials.account}",
                    RequestDatetime: "${DateTime.now()}") {
                REQUEST_DATA() {
                    FLOOD_REQUEST(MISMOVersionID: "2.4", _ActionType: "Original") {
                        _PRODUCT(_CategoryDescription: "Flood") {
                            _NAME(_Identifier: "FL")
                        }
                        BORROWER(_FirstName: "${json.loanInfo.borrower.firstName}",
                                _LastName: "${json.loanInfo.borrower.lastName}")
                        MORTGAGE_TERMS(LenderCaseIdentifier: "${json.loanInfo.caseNumber}")
                        PROPERTY(_StreetAddress: "${json.loanInfo.property.address1}",
                                _City: "${json.loanInfo.property.city}",
                                _State: "${json.loanInfo.property.state}",
                                _PostalCode: "${json.loanInfo.property.zip}") {}
                    }
                }
            }
        }

        writer.toString()
    }
}

Lines 4-6 up at the top are the only overhead that is needed before we get down to business.  Line 4 converts the incoming JSON-formatted String into an interpreted structure, ready to be queried.  Lines 5 and 6 create a MarkupBuilder ready to receive the XML structure that needs to be expressed.

Lines 8 through 28 express the XML structure using Groovy's wonderful XML syntax.  Notice that the values going into the attributes are GStrings referencing the data embedded in the parsed JSON.  Simple bean path notation, which is easy for the maintainer to understand.  And, if you need to express loops or more advanced transformations, you have the full power of Groovy to get it done.

Finally, line 30 expresses the XML as a String, which is what the method signature expects.

If you look at the test associated with this class, you can see that the JSON has two high-level blocks: "credentials" and "loanInfo".  While "loanInfo" would be the kind of information you would expect the business app to provide, you would hope that the web service credentials were coming from somewhere else.  In a future post, I'll demonstrate adding the credentials via a content enricher.

The code for this example is on github at https://github.com/swfinch1277/SpringMinutes-Int.

2 comments:

  1. Instead of "${json.credentials.username}" you should just write json.credentials.username, no need to use a GString to just put a variable value!

    Otherwise, nice usage of Groovy's JSON and XML builtin support!

    ReplyDelete
  2. Ah, very good point. By wrapping the variable lookups in GStrings I incur a lot of needless overhead.

    However, if I remove the variable references from the strings, the code does not appear in blue in the blog post. So, do the benefits outweigh the costs? ;)

    ReplyDelete