dataweave script to iterate over a nested array of string literals - mule

So I have the following payload :
<?xml version="1.0" encoding="utf8" ?>
<Output>
<Error>
<Status>0</Status>
<Details>No errors</Details>
</Error>
<Synopsis>
<Count>451</Count>
</Synopsis>
<BankAccounts>
<BankAccount AcctNo="103" CustName="Frank" BalanceAmount="" Inactive="N" NoOfAccounts="1" >
<Addresses>
<Address>ABC</Address>
<Address>XYZ</Address>
</Addresses>
</BankAccount>
<BankAccount AcctNo="101" CustName="Jane" BalanceAmount="10005" Inactive="N" NoOfAccounts="1" >
<Addresses>
<Address>LMN</Address>
<Address>QWE</Address>
</Addresses>
</BankAccount>
</BankAccounts>
</Output>
I need to convert it into the following json :
[
{
"accountNumber": "103",
"customerName": "Frank",
"balanceAmount": "",
"inactive": "N",
"noOfAccounts": "1",
"address": [
"ABC","XYZ"
]
},
{
"accountNumber": "101",
"customerName": "Jane",
"balanceAmount": "10005",
"inactive": "N",
"noOfAccounts": "1",
"address": [
"LMN","QWE"
]
}
]
I am nearly there using the below dataweave script:
%dw 2.0
output application/json writeAttributes=true
---
payload.Output.BankAccounts.*BankAccount map {
accountNumber: $.#AcctNo,
customerName: $.#CustName,
balanceAmount: $.#BalanceAmount,
inactive: $.#Inactive,
noOfAccounts: $.#NoOfAccounts,
address: $.Addresses.*Address map (value,index)->{
}
}
However I have not been able to convert the Addresses element ( which contains multiple Address elements into a string array like this :
"address": [
"LMN","QWE"
]
Am struggling with the last part , any ideas ?

Note that XML does not has the concept of an array. Addresses is just an element that has several address child elements.
You can use the multivalued selector directly. It will return an array of the matching keys values. Map is only needed if you want to further transform each element.
$.Addresses.*Address

You can use the value from the address array without using {}. DW creates an object when curly braces are used.
%dw 2.0
output application/json
---
payload.Output.BankAccounts.*BankAccount map ((account) -> {
accountNumber: account.#AcctNo,
customerName: account.#CustName,
balanceAmount: account.#BalanceAmount,
inactive: account.#Inactive,
noOfAccounts: account.#NoOfAccounts,
address: account.Addresses.*Address map ((value)-> value)
})

Related

Need to change csv header from camel case to snake case in dataweave

I am working on a pipeline to dynamically dump all columns from the salesforce object to the S3 bucket.
I get all columns for a salesforce object using describe object API. I store all columns into a variable and then create a big SOQL query out of it and submit a bulk query job v2.
Now, this is the main problem. The Column name I am getting from the salesforce connector is in camelCase
[{
"Id": 123,
"FirstName": "Manual",
"MasterRecordId__c" :"abc"
},
{
"Id": 456,
"FirstName": "John",
"MasterRecordId__c" :"def"
}]
But I want column names to be in snake case
[{
"Id": 123,
"first_name": "Manual",
"master_record_id__c":"abc"
},
{
"Id": 456,
"first_name": "john",
"master_record_id__c":"def"
}]
I understand mulesoft has an underscore function to do the same thing, but I am not able to apply any function at "key" level.
Any lead would be really helpful. Please let me know for any questions.
You just have to use mapObject along with the underscore function
%dw 2.0
import underscore from dw::core::Strings
output application/json
---
payload map ((item) ->
item mapObject ((value, key) -> {
(underscore(key)): value
})
)
In case you want Id field to remain as it is, give a try like below:
%dw 2.0
import * from dw::core::Strings
output application/json
---
payload map ($ mapObject ((value, key, index) -> if (capitalize(key as String) == key as String)
{
(key): value
}
else
{
(underscore(key)): value
}))

Dataweave - how to accumulate json response from http request connector in for each component (Mule 4)

I consume a HTTP request and I need to save and accumulate the JSON response with out any transformation in a variable, I do that but I dont know why the accumulate it correctly, could you please tell me how can I solve that problem.
Json Response By Iteration:
Iteration 1:
{
"orderId": "11111",
"status": "false",
"receivedAt": "2022-07-28T22:45:12.175Z",
"createdAt": "2022-07-28T22:45:12.175Z",
}
Iteration 2:
{
"orderId": "22222",
"status": "false",
"receivedAt": "2022-07-28T22:45:27.907Z",
"createdAt": "2022-07-28T22:45:27.907Z"
}
Dataweave: (csv Payload is the name of the variable where the values accumulated)
%dw 2.0
output application/json
---
if ( vars.counter == 1)
( payload )
else
( vars.csvPayload ) ++ payload
Variable Result:
{
"orderId": "11111",
"status": "false",
"receivedAt": "2022-07-28T22:45:12.175Z",
"createdAt": "2022-07-28T22:45:12.175Z",
"orderId": "22222",
"status": "false",
"receivedAt": "2022-07-28T22:45:27.907Z",
"createdAt": "2022-07-28T22:45:27.907Z"
}
Variable Expected:
[
{
"orderId": "11111",
"status": "false",
"receivedAt": "2022-07-28T22:45:12.175Z",
"createdAt": "2022-07-28T22:45:12.175Z",
},
{"orderId": "22222",
"status": "false",
"receivedAt": "2022-07-28T22:45:27.907Z",
"createdAt": "2022-07-28T22:45:27.907Z"
}
]
NOTE: I don't know why the JSON responses of each iteration are joined in the same object and not as a different object in an array.
The incorrect output is easy. You are concatenating an object to another object. In that case the operator ++ "extracts all the key-values pairs from each object, then combines them together into one result object." according to the documentation. The parenthesis are totally unneeded.
Setting the output to JSON in each iteration -I'm assuming this is inside a foreach- is inefficient since it requires formatting the output to JSON in each iteration only to parse it again in the following one. I recommend to use application/java -which doesn't require parsing/formatting- inside a loop and after the loop convert the entire output to JSON in one go.
You should use an array to hold the values, so assign an empty array ([]) to the variable before the foreach loop to initialize it. Then the counter is unneeded since you can just add elements to the array:
%dw 2.0
output application/java
---
vars.allOrders ++ payload
Then after the foreach just transform the array to JSON:
%dw 2.0
output application/json
---
vars.allOrders
Your requirement is a very common scenario in integrations.
Assuming you are using HTTP Requester inside a for-each loop and initialized the variable to hold the final Payload (lets say var finalPayload = []) before the loop, you have to keep on updating the same variable (append the new data in the same array variable like below) with the response payload received from HTTP Requester for each iteration.
var finalPayload << payload
Ultimately check the finalPayload outside for-each loop, that will give you your desired result.

Return elements of a JSON object as an array

I have an XML file that is converted to a JSON object by a TRANSFORM MESSAGE:
%dw 2.0
output application/json
---
payload
The resulting JSON object has a format of:
{
"Items": {
"Item": {
"ItemId": "123",
"OrganizationId": "456",
"OrganizationCode": "ABC",
where there is one "Items" and 112 "Item". What I want is to return an JSON array contains all of the ItemIds. I am attempting to use TRANSFORM MESSAGE within a FOREACH. The FOREACH has 'payload' in the collection field and TRANSFORM MESSAGE has:
%dw 2.0
output application/json
---
myItems: payload.items.*item map (item, index) -> {
"myItemId" : item.ItemId
}
However, it always returns everything, the entire JSON object. I can't figure out if my FOREACH is wrong or if my TRANSFORM MESSAGE is wrong, but it always returns the entire JSON object.
An example of the incoming JSON would be:
{
"Items": {
"Item": {
"ItemId": "8041",
"OrganizationId": "12",
"OrganizationCode": "ABC",
},
"Item": {
"ItemId": "8050",
"OrganizationId": "12",
"OrganizationCode": "ABC",
},
"Item": {
"ItemId": "3801",
"OrganizationId": "12",
"OrganizationCode": "ABC",
}
}
}
The output should be: ["8041", "8050", "3801"]. Parse the Item elements, extract the ItemID value and create a JSON array.
Your script works nearly well. Allow me to mention that the first transformation to convert from XML to JSON is counterproductive. The next transformation will have to parse JSON to emit JSON. It is better to do it in one step, from XML to JSON. If you need the intermediate result for some other processing, it is better to convert to Java (application/java), process, then when the final output is needed, then convert to the final JSON format. Processing intermediate steps in XML or JSON just consumes more resources.
Based on that I used the following XML input:
<Items>
<Item>
<ItemId>8041</ItemId>
<OrganizationId>12</OrganizationId>
<OrganizationCode>ABC</OrganizationCode>
</Item>
<Item>
<ItemId>8050</ItemId>
<OrganizationId>12</OrganizationId>
<OrganizationCode>ABC</OrganizationCode>
</Item>
<Item>
<ItemId>3801</ItemId>
<OrganizationId>12</OrganizationId>
<OrganizationCode>ABC</OrganizationCode>
</Item>
</Items>
Script:
%dw 2.0
output application/json
---
payload.Items.*Item map (item, index) -> item.ItemId
Output:
[
"8041",
"8050",
"3801"
]
The same script will work with your JSON input too.

Mule:Dataweave Iteration not working

I am trying to take output from Salesforce & transform it to a json. here is my code:
%dw 1.0
%output application/json
payload map {
headerandlines:{ id : $.Id,
agreementLineID : $.LineItems__r.Id,
netPrice : $.LineItems__r.Price__c,
volume : $.Volume__c,
name : $.Name,
StartDate : $.Start_Date__c,
EndDate : $.End_Date__c,
poField : $.PO_Field__c,
ConsoleNumber : $.Console_Number__c,
Term : $.Term__c,
ownerID : $.OwnerId,
Unit : $.Unit__c,
siteNumber : $.Site_Num__c,
customerNumber : $.Customer_Num__c
}
}
input payload looks like this.. it is a collection of objects. Somehow after the transformation only the first object is sent & rest is clobbered.
[
{
"id": "DA0YAAW",
"LineID": [
"jGEAU",
"jBEAU",
"j6EAE"
],
"Price": [
"50000.0",
"12000.0",
"45000.0"
],
"netPrice": null,
"volume": null,
"name": " Test 2.24",
"StartDate": "2017-02-17",
"EndDate": "2018-02-17",
"poField": "123456",
"ConsoleNumber": "8888888",
"PaymentTerm": "thirty (30)",
"ownerID": “abcd”,
"OperatingUnit": " International Company",
"siteNumber": null,
"customerNumber": null
},
{
"id": "a37n0000000DAMAAA4",
"LineID": [
"JunEAE",
"JuiEAE",
"KdMEAU",
"JuYEAU"
],
"Price": [
"5000.0",
"8000.0",
"5000.0",
"5000.0"
],
"netPrice": null,
"volume": null,
"name": " Test 3.6",
"StartDate": "2017-03-06",
"EndDate": "2018-03-16",
"poField": "12345",
"ConsoleNumber": "123456-",
"PaymentTerm": "30 NET",
"ownerID": “dfgh”,
"OperatingUnit": ", inc.",
"siteNumber": null,
"customerNumber": null
},
….
]
When I call this code from the browser (using API testing) I get the complete payload with multiple objects. When I call this from another API I get only one 1 object indicating it is not looping through. I can confirm that the payload has multiple objects . Is there anything I am missing in terms of looping through this code to extract multiple objects? I assume that '$' notation is good enough for iteration.
#insaneyogi, your input is either incorrect or your dataweave is incorrect.
Here in the input you have specified id in the small. but in dataweave, it is mentioned in capital.
I think the problem here is with your Lineitem and Price type elements. They are collection within and element. In your data mapping $. will take care of the outer object. However, i think the mapping like LineItems__r.Price__c is not correct. It should have proper index , probably LineItems__r.Price__c[0]. Please try that and it should work. First change the input with single element for price or line-item and test.
It looks like the agreementLineID and netPrice are arrays and you need to loop through them with a map operator within the bigger outer map to get all the line items. That should work.

How to get unnamed object instead of an array in case of only one result in Dataweave

We have a transformation with Dataweave which processes a list of objects. We get a json response like that:
{"hotels": [{
"name": "Hotel Oslo",
"propertyCode": "12345",
"currency": "NOK"
},
{
"name": "Hotel Stockholm",
"propertyCode": "12346",
"currency": "SEK"
}]}
However, in the case of only 1 response, we want to have the following response:
{"name": "Hotel Stockholm",
"propertyCode": "12346",
"currency": "SEK"}
We are generating the response like this:
{
hotels: payload.rows map ((row , indexOfRow) -> {
name: row.content.companyName.content,
propertyCode: row.content.propertyCode.content,
currency: row.content.currencyCode.content
})}
What should we put as a condition so that we do not get an array in case of 1 result?
Try this:
%dw 1.0
%output application/json
%function makeHotel(row) {
name: row.name,
propertyCode: row.propertyCode,
currency: row.currency
}
---
{
hotels: payload.rows map ((row , indexOfRow) -> makeHotel(row))
} when ((sizeOf payload.rows) != 1)
otherwise makeHotel(payload.rows[0])
It will give you an empty array on empty input, the simple object for one input and the structure with array when you have more than one input row.
(For test purposes, with a slightly differnt input structure, but the general solution should be clear.)