Looking for a particular data in JSON hashmap - mule

I need to look for AccountNo field value in different json payloads. AccountNo can exist at any level in json payload. I need to check if AccountNo exists and then print value in logger.
I am using below enricher but I want to iterate through the Hashmap in mule to check if AccountNo key exists anywhere and then get the value.
Also please suggest if any other way to parse json itself.
In xpath "//AccountNo" will look for AccountNo in whole xml document. I am after something similar.
<enricher target="#[flowVars.myJsonMap]" doc:name="Message Enricher">
<json:json-to-object-transformer returnClass="java.util.HashMap" doc:name="JSON to Object"/>
</enricher>
<logger message="#[flowVars.myJsonMap.employees[0].AccountNo]" level="INFO" doc:name="Logger"/> </flow>
Please find below example json payloads
{
"Account": {
"AccountName": "John",
"AccountNo": "4234324"
}
}
{
"Order": {
"OrderId": "34234242",
"ServiceOrder": [
{
"AccountNo": "231232",
"ServiceOrderId": "54654698787"
},
{
"AccountNo": "231232",
"ServiceOrderId": "78979879797"
}
]
}

Since its a hashmap
you can use yourmap.get(keyvalue) to get value.
<logger message="#[flowVars.myJsonMap.yourkey?]" level="INFO" doc:name="Logger"/>

From your question, I assume the following:
Your payload contains nested Maps and Lists along with other Objects
AccountNo may exists at any level of your payload
You can use a Groovy transformer to recursively parse your payload and collect all existing AccountNo in a List:
//recursively parse any Map or List in myObject
//and store AccountNo objects accountNoList
def parseObject(myObject, accountNoList) {
if (myObject instanceof java.util.Map) {
//if Map, check for AccountNo field
if(myObject.AccountNo != null){
accountNoList.add(myObject.AccountNo)
}
//seek for more AccountNo in Map
for(e in myObject) {
parseObject(e.value, accountNoList)
}
} else if (myObject instanceof java.lang.Iterable) {
//if Iterable, parse each values
for (value in myObject) {
parseObject(value, accountNoList)
}
}
}
myJsonPayload = message.getInvocationProperty('myJsonPayload')
myAccountNoList = [] //init an empty list which will be filled
parseObject(myJsonPayload, myAccountNoList)
return myAccountNoList
Then simply use a Logger.

if you are not dealing with large set of json data, just convert the json to XMl using JSONToXMLTransformer and then, use XPATH to look for the data anywhere in the document. This way, you could avoid writing any additional code to parse the json file.

Related

Mule 4: Replace recurring node value in original payload

I have a recurring node/array in the JSON payload. I need to use value of the wonumber to call another service and the value from this service response has to be used to replace a code value on original message.
{
"element1": "text1",
"element2": "text2",
"element3": "text3",
"InvoiceItemDTO": [
{
"code": "",
"itemNo": "1",
"wonumber": 123
},
{
"code": "",
"itemNo": "2",
"wonumber": 456
}
]
}
The service response will give value for code field, which has to be copied back on original payload code value.
I have applied for each loop on the recurring node to make the call to service for each block in ItemDTO. But I am concerned about putting service response value field back on the original payload at the same index for which the service call was made.
If service call returns code value as abc1, def2 for above payload, then expected final payload is:
{
"element1": "text1",
"element2": "text2",
"element3": "text3",
"InvoiceItemDTO": [
{
"code": "abc1",
"itemNo": "1",
"wonumber": 123
},
{
"code": "def2",
"itemNo": "2",
"wonumber": 456
}
]
}
Any recommendations on how this can be achieved?
the for-each scope does not change the payload. If you want to retain some information gathered during execution of for-each you will need to store that data in a variable. As per the official doc
Every execution of the For Each scope starts with the variables and values from the previous execution of the block. New variables or modifications to existing variables that take place when processing one element are visible during the processing of another element. These changes to variables continue to be available outside the For Each scope.
For your use case you can do the following (Just a pseudo code)
Collect all unique wonumber in an array and store it in a variable
<set-variable variableName="wonumbers" value="#[payload.InvoiceItemDTO.wonumber distinctBy $]"/>
Loop through this variable in the for-each scope
After collecting the code for the wonumber store that as a map in a variable so that you can access it later.
<foreach collection="#[vars.wonumbers]">
<set-variable variableName="currentWonumber" value="#[payload]" />
<do-your-thing.... />
<set-variable variableName="wonumbersToCodeMapping" value="#[(vars.wonumbersToCodeMapping default {}) ++ (vars.currentWonumber): payload.code //however you are getting the code]"/>
</foreach>
Using the wonumbersToCodeMapping you can update the payload using update operator
%dw 2.0
output json
---
payload update {
case .InvoiceItemDTO ->
$ map ((item) -> item update {
case .code -> vars.wonumbersToCodeMapping[item.wonumber as String]
})
}

How to remove or trim special characters/prefixes from payload in Mule 4

I need to trim/remove the special characters from a payload.
example- input-
�~�{
"Employee": "Sara",
"number": {
"id": "d1q2",
"designation": "CYUL"
}
}
output-
{
"Employee": "Sara",
"number": {
"id": "d1q2",
"designation": "CYUL"
}
}
I am trying to remove the before characters of "Employee" before curly braces in the beginning whatever is present should be removed and just a JSON structure should be present starting from curly braces and before whatever comes should be removed tried everything unable to do. Please help me in resolving my issue.
You can't read the payload as JSON because those characters are not valid in JSON and the DataWeave parser will throw an error. If you can read the payload as text/plain then you can use DataWeave to remove the 4 extra characters from the beginning and re read the remaining data as a JSON, and set the output to JSON too.
%dw 2.0
output application/json
---
read(payload[4 to -1], "application/json")
If you already read the payload you could try setting its MIME type before executing the DataWeave Transform: <set-payload value="#[payload]" mimeTpy="text/plain"/>.

Use each element of var to make a http request in Mule

I have a collection of matches saved to a var. Each match has a gameId. I want to use this gameId to make an API call.
I set up a for each scope using matches with a counter var.
<set-variable value="#[payload]" doc:name="Set Matches" doc:id="bb1093b0-3dd9-4515-8f42-dd496f00477b" variableName="matches"/>
<set-variable value="#[[]]" doc:name="Deaths" doc:id="a7341e19-07f8-418d-b39a-5bc415e75fba" variableName="deaths"/>
<foreach doc:name="For each match" doc:id="506fa944-d46f-4684-9c75-73088265ca80" collection="vars.matches" rootMessageVariableName="matches">
<http:request method="GET" doc:name="GetTimeline" doc:id="63708489-4d10-4e79-b5aa-c8fc723e80c6" config-ref="HTTP_Request_configuration" path="/match/v4/timelines/by-match/{matchId}">
<http:uri-params ><![CDATA[#[output application/java
---
{
"matchId" : vars.matches[vars.counter + 1].gameId
}]]]></http:uri-params>
</http:request>
<ee:transform doc:name="MapDeaths" doc:id="a6e22968-2229-4574-8cae-44225013b9cf">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
frames: payload.frames map ( frame , indexOfFrame ) -> {
events: frame.events map ( event , indexOfEvent ) -> vars.deaths + event
}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</foreach>
Yet matchId from "matchId" : vars.matches[vars.counter + 1].gameId always evalutes to null. What am I missing there?
Edit:
I'm using Mule 4.2 and the json structure of matches looks like this:
[
{
"matches": [
{
"key": "val",
"key": val,
...
},
{
"key": "val",
"key": val,
...
},
...
Set {matchId} as Query string parameter and define request a Target variable, use that in dataweave
The problem seems to be that your payload and the expression you are using doesn't match (pun not intended). The payload is an array that contains objects, with a matches attribute. The expression apparently assumes that the payload contains a matches attribute, which contains a gameId attribute. That is the reason it returns null.
Also the intention is not clear, given that you didn't show were gameId is in your example.
Let's try with a more detailed example.
For the input:
[
{
"matches": [
{
"keyA": "val1",
"keyB": "val2"
},
{
"keyC": "val3",
"keyD": "val4"
}
]
}
]
The expression payload.matches.keyA returns null.
You could do payload[0].matches[0].keyA to obtain "val1".
This assumes there data is from the first element of the array and you want to use only one key from one of the matches. Of course if you need to iterate on the different arrays levels you will need to use map or group, but it is a different use case.

Dataweave script works for preview, fails in actual use

Using Studio 6.1.0
Runtime 3.8.2ee
I've written a dataweave script that works fine when I preview the results in the Transform Message window, but when I actually run my application I get an error which suggests a problem with the dataweave script.
This is the error:
Invalid input "[\n \t\t({\n \t\t\teffectiveTerm:
template.effectiveTerms.effectiveFromTerm,\n \t\t\tattributeCode: "VARU"\n \t\t} as :object
{ class: "edu.mit.courseleaf.domain.backfill.OldAttribute" })\n \t\twhen
template.units.variableUnits == "Y",", expected flatten, typeOf, using, unzip, capitalize,
avg, min, lower, upper, max, singularize, not, dasherize, ordinalize, camelize, trim, sizeOf,
value, pluralize, sum or underscore (line 16, column 24)
(java.util.concurrent.ExecutionException). Message payload is of type: HashMap
Flow with dataweave script, input for preview, json for actual execution are included below. A couple of comments:
The part of the dataweave script referenced in the error is for the attributes property. The goal here is to set the property to a list of OldAttribute objects. The contents of the list depend on two separate element in the source data: the presence of a subjectTemplate.Units.variableUnits == "Y" and a subset of values in the attributes element.
I know we could go directly from JSON to POJO. In the full application, we're doing additional processing on the intermediary HashMap so we want to retain the two step conversion.
edu.mit.courseleaf.domain.backfill.OldAttribute and edu.mit.courseleaf.domain.backfill.SourceSubject are POJOs with default constructors and the proper getters and setters for the properties we're using.
Flow with dataweave script:
<flow name="temp-json-to-map-to-pojo-example-for-so">
<json:json-to-object-transformer returnClass="java.util.HashMap" doc:name="JSON to Object"/>
<set-payload value="#[payload]" mimeType="*/*" doc:name="Set Payload"/>
<dw:transform-message doc:name="Transform Message">
<dw:input-payload doc:sample="sample_data/map.dwl"/>
<dw:set-payload><![CDATA[%dw 1.0
%input payload application/java
%output application/java
%function passThroughAttributes(attributeList) attributeList - "RPT" - "SWE" - "FSEM"
---
using (template = payload.subjectTemplate
, attributes = payload.subjectTemplate.attributes
)
{
subjectCode:template.mainSubjectCode,
subjectNumber:template.mainSubjectNumber,
attributes: flatten [
({
effectiveTerm: template.effectiveTerms.effectiveFromTerm,
attributeCode: "VARU"
} as :object { class: "edu.mit.courseleaf.domain.backfill.OldAttribute" })
when template.units.variableUnits == "Y",
passThroughAttributes(attributes) map (
{
attributeCode: $,
effectiveTerm: template.effectiveTerms.effectiveFromTerm
}
) as :object { class: "edu.mit.courseleaf.domain.backfill.OldAttribute" }
]
}as :object{class : "edu.mit.courseleaf.domain.backfill.SourceSubject"
]]></dw:set-payload>
</dw:transform-message>
</flow>
Sample input for preview:
{
subjectTemplate: {
subjectTemplateId: "43C9C99A88B76AFBE053282701126517",
mainSubjectCode: "HAA",
mainSubjectNumber: "T101",
subjectLevel: "U",
effectiveTerms: {
effectiveFromTerm:"2017SP",
effectiveThruTerm: "999999"
},
units: {
variableUnits: "Y",
lectureUnits: 9,
labUnits: 3,
prepUnits: 0,
totalCreditLimitUnits: 0
},
attributes: ["RPT", "XLIS"]
}
}
Screenshot of preview showing attributes property correctly populated
Actual JSON input for flow
{
"subjectTemplate": {
"subjectTemplateId": "43C9C99A88B76AFBE053282701126517",
"mainSubjectCode": "HAA",
"mainSubjectNumber": "T101",
"subjectLevel": "U",
"effectiveTerms": {
"effectiveFromTerm": "2017SP",
"effectiveThruTerm": "999999"
},
"units": {
"variableUnits": "Y",
"lectureUnits": 9,
"labUnits": 3,
"prepUnits": 0,
"totalCreditLimitUnits": 0
},
"attributes": ["RPT", "XLIS"]
}
}
Please try with group by in dataweave for grouping all the same category product in one array then do further clean as per your requirement. You can refer here for the details.

How to catch NULL values in payload using MEL

How can i catch null values in payload using MEL, i am using mule 3.4, most of my payload is in JSON and sometimes XML.
{
"username": "DEADPOOL",
"text": "chimichanga",
"val":null
}
You can use choice component.
And try this flow :
<choice doc:name="Choice">
<when expression="#[payload.'username' == null]">
{your code}
</when>
<otherwise>
</otherwise>
</choice>
In this case, you can reference the part of the payload with payload['username'] which would equal DEADPOOL
So you could do something like this
if(payload['username'] == null) {
//do something if it's null
}
else {
//it's not null
}