Interrogate structured data paths

   Exchange Pre-configured Maestro services.  |   Platform Developer |  All versions   This feature is related to v5.1 and higher.

This article relates to use of the com.avoka.tm.util.Path class, available in the Temenos Journey Manager (TJM) Fluent API.

One of the most common tasks when writing Groovy services is to read structured data from either JSON or XML payloads. This is essential if you are calling third-party APIs as most APIs return structured data in one of these formats.

The TJM Fluent API contains a very handy utility class that makes interrogating data of this type very easy, also overcoming a limitation of Static compilation and type checking.

JSON

This example demonstrates how the Path utility class can be used to interrogate JSON data.

String jsonData = '''{
    "locality": { "state": "NSW", "region": "Sydney" },
    "resultCount": 3,
    "resultList": [
        { "brewer": "4 Pines",         "productName": "Kolsch",   "rating": 3.9 },
        { "brewer": "Pirate Life",     "productName": "IIPA",     "rating": 4.6 },
        { "brewer": "Modus Operandi",  "productName": "Red IPA",  "rating": 4.3 }
    ]
}'''

Path jsonPath = new Path(jsonData)

// Get the value of a json data element
println 'resultCount: ' + jsonPath.val('resultCount')
println 'locality region: ' + jsonPath.val('locality.region')

// Get the list of paths at a specified location
println 'resultList size: ' + jsonPath.paths('resultList.*').size()

// Iterate through a JSON array
println '>> ALL RESULTS'
jsonPath.paths('resultList.*').each{
    println '    ' + it.val('brewer') + ' - ' + it.val('productName') + ' - ' + it.val('rating')
}

// Iterate through a filtered JSON array
int minRating = 4
println '>> Rating > ' + minRating
jsonPath.paths('resultList.*').findAll{ it.val('rating') > minRating }.each{
    println '    ' + it.val('brewer') + ' - ' + it.val('productName') + ' - ' + it.val('rating')
}

// Iterate through an ordered JSON array
println '>> Ordered by rating'
jsonPath.paths('resultList.*').sort{ - it.val('rating') }.eachWithIndex{ it, i ->
    println '    ' + (i+1) + '. ' + it.val('brewer') + ' - ' + it.val('productName') + ' - ' + it.val('rating')
}

XML

This example demonstrates using Path the interrogate XML data. To learn more, see Interrogating XML Content Containing Namespaces.

String xmlData = '''
    <XmlData>
        <locality><state>NSW</state><region>Sydney</region></locality>
        <resultCount>3</resultCount>
        <resultList>
            <result><brewer>4 Pines</brewer><productName>Kolsch</productName><rating>3.9</rating></result>
            <result><brewer>Pirate Life</brewer><productName>IIPA</productName><rating>4.6</rating></result>
            <result><brewer>Modus Operandi</brewer><productName>Red IPA</productName><rating>4.3</rating></result>
        </resultList>
    </XmlData>'''

Path xmlPath = new Path(xmlData)

// Get the value of a xml data element using XPath
println 'resultCount: ' + xmlPath.val('//resultCount')
println 'locality region: ' + xmlPath.val('//locality/region')

// Get the list of paths at a specified XPath location
println 'resultList size: ' + xmlPath.paths('//resultList/result').size()

// Iterate through an XML array
println '>> ALL RESULTS'
xmlPath.paths('//resultList/result').each{
    println '    ' + it.val('//brewer') + ' - ' + it.val('//productName') + ' - ' + it.val('//rating')
}

// Iterate through a filtered XML array
int minRating = 4
println '>> Rating > ' + minRating
xmlPath.paths('//resultList/result').findAll{ new Double(it.val('//rating')) > minRating }.each{
    println '    ' + it.val('//brewer') + ' - ' + it.val('//productName') + ' - ' + it.val('//rating')
}

// Iterate through an ordered XML array
println '>> Ordered by rating'
xmlPath.paths('//resultList/result').sort{ - new Double(it.val('//rating')) }.eachWithIndex{ it, i ->
    println '    ' + (i+1) + '. ' + it.val('//brewer') + ' - ' + it.val('//productName') + ' - ' + it.val('//rating')
}

POJO

This example demonstrates using the Path utility class to interrogate an object graph using Plain Old Java Objects (POJOs).

Assume the following classes:

class Locality{
    String state, region
}
class Product {
    String brewer, productName
    Double rating
}
class Result {
    Locality locality
    int resultCount
    List<Product> resultList
}

The following code snippet shows use of Path interrogate an object graph consisting of instances of these classes:

Result pojoData = new Result()
pojoData.resultCount = 3
pojoData.locality = new Locality(state: "NSW", region: "Sydney")
pojoData.resultList = [
    new Product(brewer: "4 Pines", productName: "Kolsch", rating: 3.9),
    new Product(brewer: "Pirate Life", productName: "IIPA", rating: 4.6),
    new Product(brewer: "Modus Operandi", productName: "Red IPA", rating: 4.3)
]

Path pojoPath = new Path(pojoData)

// Get the value of a pojo variable
println 'resultCount: ' + pojoPath.val('resultCount')
println 'locality region: ' + pojoPath.val('locality.region')

// Get the list of paths at a specified location
println 'resultList size: ' + pojoPath.paths('resultList').size()

// Iterate through an Object array
println '>> ALL RESULTS'
pojoPath.paths('resultList').each {
    println '    ' + it.val('brewer') + ' - ' + it.val('productName') + ' - ' + it.val('rating')
}

// Iterate through a filtered Object array
int minRating = 4
println '>> Rating > ' + minRating
pojoPath.paths('resultList').findAll { it.val('rating') > minRating }.each {
    println '    ' + it.val('brewer') + ' - ' + it.val('productName') + ' - ' + it.val('rating')
}

// Iterate through an ordered Object array
println '>> Ordered by rating'
pojoPath.paths('resultList').sort { -it.val('rating') }.eachWithIndex { it, i ->
    println '    ' + (i + 1) + '. ' + it.val('brewer') + ' - ' + it.val('productName') + ' - ' + it.val('rating')
}

Output

The output from all the above code snippets is exactly the same:

resultCount: 3
locality region: Sydney
resultList size: 3
>> ALL RESULTS
    4 Pines - Kolsch - 3.9
    Pirate Life - IIPA - 4.6
    Modus Operandi - Red IPA - 4.3
>> Rating > 4
    Pirate Life - IIPA - 4.6
    Modus Operandi - Red IPA - 4.3
>> Ordered by rating
    1. Pirate Life - IIPA - 4.6
    2. Modus Operandi - Red IPA - 4.3
    3. 4 Pines - Kolsch - 3.9