How to catch NULL values in payload using MEL - mule

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
}

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]
})
}

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.

Looking for a particular data in JSON hashmap

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.

Mule ESB Multiple when conditions in Dataweave

I am using dataweave, due to sales taxes in each state, I need to assign a each customer from a specific state to a pre-defined user.
However when I create this in dataweave I end up with this as my XML:
<Customer>
<Number>
<Number>
<Number>
<Number>WEB002</Number>
</Number>
</Number>
</Number>
I just need one 'number' node underneath the 'customer' node.
Here is my dataweave:
Customer:{
Number: 'WEB001' when payload.order.billing_address.state == 'Indiana' otherwise
Number: 'WEB002' when payload.order.billing_address.state == 'Kentucky' otherwise
Number: 'WEB003' when payload.order.billing_address.state == 'Illinois' otherwise
Number: 'WEB004'
},
Have tried placing my conditions inside of brackets, no joy, have tried to wrap each condition in brackets, no joy.
Is there a better way to do this?
A very easy and simple way to do that as follows:-
<dw:transform-message doc:name="Transform Message">
<dw:input-payload doc:sample="empty.xml" />
<dw:set-payload><![CDATA[%dw 1.0
%output application/xml
---
{
Customer:
Number: "WEB001" when payload.order.billing_address.state == "Indiana"
otherwise (
"WEB002" when payload.order.billing_address.state == "Kentucky"
otherwise (
"WEB003" when payload.order.billing_address.state == "Illinois"
otherwise "WEB004"))
}
]]></dw:set-payload>
</dw:transform-message>
you can put the mapping between state and number in a map/dictionary which would improve the readability like this:
%var numbers = {
Indiana: 'WEB001',
Kentucky: 'WEB002',
Illinois: 'WEB003'
}
this is what your dataweave would look like:
%dw 1.0
%output application/xml
%var defaultNumber = 'WEB004'
%var numbers = {
Indiana: 'WEB001',
Kentucky: 'WEB002',
Illinois: 'WEB003'
}
---
Customer: {
Number: numbers[payload.order.billing_address.state]
default defaultNumber
}
and here is a screenshot containing sample data and the resulting preview:

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.