Navigate to path returned from diff function in dataweave on original payload - mule

I am using dataweave diff function to compare two json objects. Diff is functioning correctly and returning the items that have changed, but it isn't returning anything but the differences. It is returning the path of the item that has changed, but I am not finding a good way to navigate to a string based path. I am only showing one group in the example but there could be many groups by different unique names.
{
"email": "someone#somewhere.com",
"frequency": 1,
"group1": [],
"group2": [],
etc...
)
So here is an example of what I am trying to describe:
Payload 1
{
"email": "someone#somewhere.com",
"frequency": 1,
"group1": [
{
"name": "item1",
"enabled": true
},
{
"name": "item2",
"enabled": true
},
{
"name": "item3",
"enabled": true
}
]
}
Payload 2:
{
"email": "someone#somewhere.com",
"frequency": 1,
"group1": [
{
"name": "item1",
"enabled": true
},
{
"name": "item2",
"enabled": true
},
{
"name": "item3",
"enabled": false
}
]
}
In this case diff between payload 1 and payload 2 would return
{
"matches": false,
"diffs": [
{
"expected": "true",
"actual": "false",
"path": "(root).group1[2].enabled"
}
]
}
I need to reach back into the original payload and navigate to (root).order[2] to get the name key/value pair. What is the best way to do this?
So at the end of the day I need the payload to be:
[
{
name: "item3",
enabled: false,
level: 1
}
]
I appreciate any assistance you can offer. I am also doing a map of the payload level so I know which piece of data to update when I traverse the results. So in this case the level would be 1 based on the fact that is an array off the main object.
This is my current dataweave I just need the first element to be dynamic based on the path that the diff returns
%dw 2.0
output application/json
---
payload.diffs map (item, index) -> {
name: initialPayload.group1[2].name, <-needs to be dynamic based on path
value: (item.actual) replace "\"" with "",
level: sizeOf(item.path splitBy ".")-1
}

I was able to resolve the issue with this weave.
%dw 2.0
fun GetValue(group: String, level: Number, itemkey: String) =
GetKeyValue((((group) splitBy /\./ reduce (e, acc = vars.initialPayload) -> acc[e])[level]), itemkey)
fun GetKeyValue(obj: Object, itemKey: String) =
(entriesOf(obj) filter (item) -> item.key as String == itemKey).value[0]
output application/json skipNullOn="everywhere"
---
{
changes: payload.diffs map (item, index) -> {
name:
if (sizeOf(item.path splitBy ".") - 1 == 1)
((item.path splitBy ".")[1])
else
GetValue((((item.path splitBy ".")[1]) splitBy "[")[0], (((item.path splitBy ".")[1]) splitBy "[")[1] replace "]" with "", "name"),
value: item.actual replace "\"" with "",
level: (sizeOf(item.path splitBy ".") - 1),
}
}

Related

Transformation of Json array in Dataweave

How to write Dataweave transformation in Anytime Studio for given input and output of Json array.
Input:
{
"result": [{
"Labels": [{
"value": [{
"fieldName": "firstName",
"value": "John"
},
{
"fieldName": "lastName",
"value": "Doe"
},
{
"fieldName": "fullName",
"value": "John Doe"
}
]
}]
}]
}
Output:
{
"result": [{
"Labels": [{
"value": [{
"firstName": "John",
"lastName": "Doe",
"fullName": "John Doe"
}]
}]
}]
}
https://docs.mulesoft.com/dataweave/2.4/dw-core-functions-reduce Reduce function might be the one should be used
Thank you in advance
You can just use map to map all the arrays to required format. For the value part you can map the values as fieldName: value array and deconstruct them to an object by wrapping the array around parentheses
%dw 2.0
output application/json
---
{
result: payload.result map ((item) -> {
Labels: item.Labels map ((label) -> {
value: [
{
(label.value map ((field) ->
(field.fieldName): field.value
)) //wrap the array, i.e. lavel.value map ... in parentheses so that it will give you individual key pair.
}
]
})
})
}
You can try below if you are aware that the keyNames will not change:
%dw 2.0
output application/json
---
payload update {
case res at .result -> res map (res, resIndex) -> (res update {
case lbl at .Labels -> lbl map (lbl, lblIndex) -> (lbl update {
case val at .value -> [
(val reduce ((item, acc = {}) -> acc ++ {
(item.fieldName): (item.value)
}))
]
}
)
}
)
}
Here's 2 caveats and a solution. Your input and output files, both are not valid JSON.
Input file, in your "result" object, "Labels" need curly braces {} since they are objects. Key-value pairs should look like this {key:value} not like that key:value
Output file, inside your "value" arrays, key-value pairs need to have the curlies {key:value}
So here's a valid JSON version of your input
{
"result": [
{"Labels": [
{
"value": [
{"fieldName": "firstName","value": "John"},
{"fieldName": "lastName","value": "Doe"},
{"fieldName": "fullName","value": "John Doe"}
]
}
]},
{"Labels": [
{
"value": [
{"fieldName": "firstName","value": "John"}
]
}
]}
]}
Here's a solution
%dw 2.0
import keySet from dw::core::Objects
// this is "result"
var layer1key = keySet(payload)[0]
// this is "Labels" and grabs the first Labels, so assumes Labels doesn't change
var layer2 = payload[layer1key]
var layer2key = keySet(layer2[0])[0]
// this is "value"
var layer3 = layer2[layer2key]
var layer3key = keySet(layer3[0][0])[0]
// this is "fieldName" and "value"
var layer4 = layer3 map (x) -> x['value']
var data1 = ((layer1key) : layer4 map (x) -> {
(layer2key): x map (y) -> {
(layer3key): y map (z) -> {
(z['fieldName']):z['value']
}
}
})
output application/json
---
data1
And a valid JSON version of your output
{
"result": [
{
"Labels": [
{
"value": [
{
"firstName": "John"
},
{
"lastName": "Doe"
},
{
"fullName": "John Doe"
}
]
}
]
},
{
"Labels": [
{
"value": [
{
"firstName": "John"
}
]
}
]
}
]
}

Compare multiple fields in a single array and have conditions on their values

I need to compare two fields in a array and replace their values with different data and output in separate objects
Here is my sample input array:
{
"data": [
{
"house": "house1",
"condition": "bad",
"age": "old",
},
{
"house": "house2",
"condition": "good",
"age": "new",
}
]
}
Output should be
{
"data": [
{
"house": "house1",
"condition": "repair"
},
{
"house": "house1",
"age": "over50",
},
{
"house": "house2",
"condition": "No repair",
},
{
"house": "house2",
"age": "recent"
}
]
}
If condition is "bad" I need to replace with "repair" else if condition is "good" I need to replace with "No repair".
Same type of logic. for the age field. If age is "old" I need to replace with "Over50" and if age is "new" I need to replace with "recent". The two fields(age and condition) are going to be in every iteration
As aled suggested, Map would do this along with OrderBy.
%dw 2.0
output application/json
var a=payload.data
---
"data":(a map{
"house": $.house,
"condition":if (($.condition)=="bad") "repair" else "No repair"
} ++ (a map{
"house": $.house,
"age":if (($.age)=="old") "over50" else "recent"
})orderBy $.house)
Output
{
"data": [
{
"house": "house1",
"condition": "repair"
},
{
"house": "house1",
"age": "over50"
},
{
"house": "house2",
"condition": "No repair"
},
{
"house": "house2",
"age": "recent"
}
]
}
Alternative solution with a single map (you map to an array of the 2 objects required and then flatten the result):
%dw 2.0
output application/json
fun mapCondition(cond : String) = if (cond == "bad") "repair" else "No repair"
fun mapAge(age : String) = if (age == "old") "over50" else "recent"
---
{
data : flatten(payload.data
map ((item, index) ->
[
{
house: item.house,
condition: mapCondition(item.condition)
},
{
house: item.house,
age: mapAge(item.age)
}
]))
}
Use map() over data array and use if conditions to transform the field of each element inside the map. Seems pretty direct.
You can also apply map and flatten with a single call to flatMap.
%dw 2.0
output application/json
---
data: payload.data flatMap [
{
house: $.house,
condition: $.condition match {
case 'bad' -> 'Repair'
else -> 'No Repair'
}
},
{
house: $.house,
age: $.age match {
case 'old' -> 'over50'
else -> 'new'
}
}
]

isAlpha or isAlphanumeric in dataweave not working as expected

I have 2 keys in payload for which I want to validate the values, here are the scenarios
{
"System" : "ABC",
"Number" : 123.45
},
{
"System" : "ABC123",
"Number" : "123abc"
}
I want to validate if the string "System" only contains Characters and not any numbers.
I tried isAlphanumeric(payload[0].system) or contains(payload[0].system, /^[0-9]+$/ but that will still not give me correct result. how do I make sure System only has characters and not is not alphanumeric?
Same for the Number, I want to validate if the number field has any character or not? If I use isNumeric(payload[0].number) it does not validate decimal and return false if number has decimals, if I use isDecimal(payload[0].number) and if number is 1234 is does not validate that.
How do I validate these 2 fields?
Keep in mind that in your example you're mixing String and Number types for the same field; isDecimal requires a Number, and is only used to determine if the Number is a decimal number or not. When you call it you already need to know if its a Number type. If you want to be able to mix and match types:
Input:
[{
"System" : "ABC",
"Number" : 123.45
},
{
"System" : "ABC123",
"Number" : "123abc"
},
{
"System": "AAAA-AAA",
"Number": "1234"
},
{
"System": "KLDJSKL2123",
"Number": 123
},
{
"System": "KLDJSLKFJ",
"Number": "123.45"
}]
Dataweave:
%dw 2.0
import isAlpha from dw::core::Strings
var isNumericOrDecimal = (str: Number | String) ->
str match {
case is Number -> true
case str matches /^[\+\-]?\d*\.?\d+(?:[Ee][\+\-]?\d+)?/ -> true
else -> false
}
output application/json
---
payload map { System: isAlpha($.System), Number: isNumericOrDecimal($.Number) }
Output:
[
{
"System": true,
"Number": true
},
{
"System": false,
"Number": false
},
{
"System": false,
"Number": true
},
{
"System": false,
"Number": true
},
{
"System": true,
"Number": true
}
]

How to get all values where a certain Key matches in Dataweave 2.0?

Payload :
[
{
"Contacts": "123456,098765",
"Emails" : ""
},
{
"Contacts": "ABC123",
"Emails" : ""
}
]
How can I get a list of all emails from the below array of objects where the contact Id matches from each row in the payload? (Expected output below)
Variable accConts
{
"queryResponse": [
{
"Email": "test123#test.com",
"SalesforceId": "123456"
},
{
"Email": "test#test.com",
"SalesforceId": "098765"
},
{
"Email": "ABC#test.com",
"SalesforceId": "ABC123"
}
]
}
Expected Output:
[
{
"Contacts": "123456,098765",
"Emails" : "test123#test.com, test#test.com"
},
{
"Contacts": "ABC123",
"Emails" : "ABC#test.com"
}
]
HTH..
%dw 2.0
output application/json
var qResp ={
"queryResponse": [
{
"Email": "test123#test.com",
"SalesforceId": "123456"
},
{
"Email": "test#test.com",
"SalesforceId": "098765"
},
{
"Email": "ABC#test.com",
"SalesforceId": "ABC123"
}
]
}
---
payload filter ($.Contacts != null) map using (iter = $$) {
"Contacts" : $.Contacts,
"Emails": (qResp.queryResponse filter (payload[iter].Contacts contains $.SalesforceId)) reduce ((item,acc = "") -> (acc ++ "," ++ item.Email)[1 to -1]
)
}
I accepted Salim Khan's answer as he guided me in the right direction and the logic to get emails worked. I just needed to rework the map logic,
payload map (row, index) -> {
"Contacts" : row."Contacts",
"Emails" : (qResp.queryResponse filter (row."Contacts" contains $.SalesforceId)) reduce ((item,acc = "") -> (acc ++ "," ++ item.Email)[1 to -1]
),
}
Hopefully this comaprision helps
Wanted to add a slightly more succinct solution to show another approach.
%dw 2.0
output application/json
var qResp =
{
"queryResponse": [
{
"Email": "test123#test.com",
"SalesforceId": "123456"
},
{
"Email": "test#test.com",
"SalesforceId": "098765"
},
{
"Email": "ABC#test.com",
"SalesforceId": "ABC123"
}
]
}
---
payload map (value) ->
{
'Contacts':value.Contacts,
'Emails': qResp.queryResponse[?(value.Contacts contains $.SalesforceId)]..Email joinBy ", "
}

Transformation of JSON data in mule 4

I have a requirement wherein I have to convert JSON data from one format to other.
I have to fetch corresponding values of JSON array and make them a key value pair.
Below are the required details:
Input:
"Headers": {
"Header": [
{
"Key": "SellerOrganization",
"Value": "XYZ"
},
{
"Key": "SellerType",
"Value": "B2C"
},
{
"Key": "Region",
"Value": "SOUTH"
},
{
"Key": "OrderType",
"Value": "RETURN"
},
{
"Key": "InvoiceType",
"Value": ""
},
{
"Key": "EventType",
"Value": "Created"
},
{
"Key": "EntryType",
"Value": "Call Center"
}
]
}
Expected Output:
{
SellerOrganization:XYZ,
SellerType: B2C,
Region:SOUTH,
OrderType:RETURN,
InvoiceType:"",
EventType:Created,
EntryType:Call Center
}
You can use the dynamic object that it will basically do what you want.
%dw 2.0
output application/json
---
{
(payload.Headers.Header map ((item, index) -> {
(item.Key): item.Value
})
)
}
You can take advantage of reduce function here which will let you convert your array to an key, value pair object
%dw 2.0
output application/json
---
payload.Header reduce ((item, acc = {}) -> acc ++ {
(item.Key): item.Value
})