Mule4 Dataweave transformation - mule

I need to transform the below JSON
Input :-
{
"type": "donut",
"weight-unit": "lb",
"price-unit": "$/lb",
"price": 10.75,
"batters":
{
"batter":
[
{ "id": "10011", "type": "Original","weight": 500},
{ "id": "10021", "type": "Chocolate","weight": 200, "price": 11.75 },
{ "id": "10031", "type": "Blueberry", "weight": 250, "price": 11.75 },
{ "id": "10041", "type": "Devil's Food", "weight": 150}
]
},
"topping":
[
{ "id": "50011", "type": "None", "price": 0 },
{ "id": "50021", "type": "Glazed", "price": 45.23},
{ "id": "50051", "type": "Sugar", "price": 34.1},
{ "id": "50071", "type": "Powdered Sugar", "price": 21.11},
{ "id": "50061", "type": "Chocolate with Sprinkles", "price": 34.43 },
{ "id": "50031", "type": "Chocolate", "price": 87.40},
{ "id": "50041", "type": "Maple", "price": 64.11}
]
}
The output that I want is
Output :-
{
"type": "donut",
"ChocolateFlavoredGlazedDonut" : {
"weight": 200,
"unit": "kg",
"price": 56.98,
"unit": "$/kg",
},
"ChocolateFlavoredSprinklesDonut" : {
"weight": 200,
"unit": "kg",
"price": 46.18,
"unit": "$/kg",
},
"BlueberryFlavoredSugarDonut" : {
"weight": 250,
"unit": "kg",
"price": 45.85,
"unit": "$/kg",
},
"OriginalGlazedDonut" : {
"weight": 500,
"unit": "kg",
"price": 45.23,
"unit": "$/kg",
},
"OriginalMapleDonut" : {
"weight": 500,
"unit": "kg",
"price": 64.11,
"unit": "$/kg",
},
"OriginalSugarDonut" : {
"weight": 500,
"unit": "kg",
"price": 34.1,
"unit": "$/kg",
},
}
Explanantion:-
"BatterName + ToppingName" : {
"weight": 500(batter weight),
"unit": "kg"(hard coded),
"price": 34.1(batter price + topping price),
"unit": "$/kg"(hard coded,
}
For example if Batter Name is "Chocolate", then there will be 6 toppings for Chocolate batter and so on for each batter. So total batter number is 4 and topping is 8 , I want 32 items in the final output

You basically need a cross join on toppings and batters. You can use join from dw::core::Arrays to do that. It accepts the 2 arrays as input along with two joining criteria (which are inline functions). For that you can just pass a function that always returns true (or any other static value but it should be same in both criteria functions) so the function will merge every item with every item, and you will get all combos possible.
I noticed that the names of the snack after combining is not very straight forward, so I crated a separated function for that.
%dw 2.0
import join from dw::core::Arrays
import capitalize from dw::core::Strings
output application/json
fun getComboName(batterName, toppingName, snackType) =
capitalize(batterName)
++ (if(lower(batterName) != "original")("Flavoured") else "")
++ (if(lower(toppingName) != "none") capitalize((toppingName splitBy " ")[-1]) else "")
++ capitalize(snackType)
---
join(
payload.batters.batter,
payload.topping,
(a) -> true,
(a) -> true
)
reduce ((combo, acc={"type": payload."type"}) -> {
(acc),
(getComboName(combo.l."type", combo.r."type", payload."type")): {
weight: combo.l.weight,
unit: "kg",
price: (combo.l.price default 0) + (combo.r.price default 0),
unit: "\$/kg"
}
})

Related

How to get results in OPENJSON from whole json?

I have a JSON structure which has several elements with their respective values.
Each element has method.mash_temp, ingredients.malt and ingredients.hops, all of which can has multiple values.
How would I query it so I get values from each element? (there's 25 elements, in this example is only 2 for simplification)
I use sql server 2019
P.S. I'm not even sure if I'm using "elements" in te correct context, but I think you'll get what I'm trying to do
DECLARE #json nvarchar(max) = '
{
"id": 1,
"name": "Buzz",
"tagline": "A Real Bitter Experience.",
"first_brewed": "09/2007",
"description": "A light, crisp and bitter IPA brewed with English and American hops. A small batch brewed only once.",
"image_url": "https://images.punkapi.com/v2/keg.png",
"abv": 4.5,
"ibu": 60,
"target_fg": 1010,
"target_og": 1044,
"ebc": 20,
"srm": 10,
"ph": 4.4,
"attenuation_level": 75,
"volume": {
"value": 20,
"unit": "litres"
},
"boil_volume": {
"value": 25,
"unit": "litres"
},
"method": {
"mash_temp": [
{
"temp": {
"value": 64,
"unit": "celsius"
},
"duration": 75
}
],
"fermentation": {
"temp": {
"value": 19,
"unit": "celsius"
}
},
"twist": null
},
"ingredients": {
"malt": [
{
"name": "Maris Otter Extra Pale",
"amount": {
"value": 3.3,
"unit": "kilograms"
}
},
{
"name": "Caramalt",
"amount": {
"value": 0.2,
"unit": "kilograms"
}
},
{
"name": "Munich",
"amount": {
"value": 0.4,
"unit": "kilograms"
}
}
],
"hops": [
{
"name": "Fuggles",
"amount": {
"value": 25,
"unit": "grams"
},
"add": "start",
"attribute": "bitter"
},
{
"name": "First Gold",
"amount": {
"value": 25,
"unit": "grams"
},
"add": "start",
"attribute": "bitter"
},
{
"name": "Fuggles",
"amount": {
"value": 37.5,
"unit": "grams"
},
"add": "middle",
"attribute": "flavour"
},
{
"name": "First Gold",
"amount": {
"value": 37.5,
"unit": "grams"
},
"add": "middle",
"attribute": "flavour"
},
{
"name": "Cascade",
"amount": {
"value": 37.5,
"unit": "grams"
},
"add": "end",
"attribute": "flavour"
}
],
}
},
{
"id": 2,
"name": "Trashy Blonde",
"tagline": "You Know You Shouldnt",
"first_brewed": "04/2008",
"description": "A titillating, neurotic, peroxide punk of a Pale Ale. Combining attitude, style, substance, and a little bit of low self esteem for good measure; what would your mother say? The seductive lure of the sassy passion fruit hop proves too much to resist. All that is even before we get onto the fact that there are no additives, preservatives, pasteurization or strings attached. All wrapped up with the customary BrewDog bite and imaginative twist.",
"image_url": "https://images.punkapi.com/v2/2.png",
"abv": 4.1,
"ibu": 41.5,
"target_fg": 1010,
"target_og": 1041.7,
"ebc": 15,
"srm": 15,
"ph": 4.4,
"attenuation_level": 76,
"volume": {
"value": 20,
"unit": "litres"
},
"boil_volume": {
"value": 25,
"unit": "litres"
},
"method": {
"mash_temp": [
{
"temp": {
"value": 69,
"unit": "celsius"
},
"duration": null
}
],
"fermentation": {
"temp": {
"value": 18,
"unit": "celsius"
}
},
"twist": null
},
"ingredients": {
"malt": [
{
"name": "Maris Otter Extra Pale",
"amount": {
"value": 3.25,
"unit": "kilograms"
}
},
{
"name": "Caramalt",
"amount": {
"value": 0.2,
"unit": "kilograms"
}
},
{
"name": "Munich",
"amount": {
"value": 0.4,
"unit": "kilograms"
}
}
],
"hops": [
{
"name": "Amarillo",
"amount": {
"value": 13.8,
"unit": "grams"
},
"add": "start",
"attribute": "bitter"
},
{
"name": "Simcoe",
"amount": {
"value": 13.8,
"unit": "grams"
},
"add": "start",
"attribute": "bitter"
},
{
"name": "Amarillo",
"amount": {
"value": 26.3,
"unit": "grams"
},
"add": "end",
"attribute": "flavour"
},
{
"name": "Motueka",
"amount": {
"value": 18.8,
"unit": "grams"
},
"add": "end",
"attribute": "flavour"
}
],
"yeast": "Wyeast 1056 - American Ale™"
},
"food_pairing": [
"Fresh crab with lemon",
"Garlic butter dipping sauce",
"Goats cheese salad",
"Creamy lemon bar doused in powdered sugar"
],
"brewers_tips": "Be careful not to collect too much wort from the mash. Once the sugars are all washed out there are some very unpleasant grainy tasting compounds that can be extracted into the wort.",
"contributed_by": "Sam Mason <samjbmason>"
}';
Here's a query for ingredients.malt which works, but only gets data from the first element, how would I query it so I get all data?
SELECT name, amount_value, amount_unit
FROM OPENJSON(#json, '$.ingredients.malt')
WITH (
name nvarchar(30) '$.name'
, amount nvarchar(max) '$.amount' as JSON
)
CROSS APPLY OPENJSON(amount, '$')
WITH (
amount_value varchar(5) '$.value'
, amount_unit nvarchar(50) '$.unit'
)
;
Opening the JSON on a higher level than the path $.ingredients.malt, will return other categories besides malt.
You can use the json_value() function to fetch values from a JSON string.
select category.[key] as CategoryName,
json_value(ingredient.[value], '$.name') as IngredientName,
json_value(ingredient.[value], '$.amount.value') as IngredientAmount,
json_value(ingredient.[value], '$.amount.unit') as IngredientUnit
from openjson(#json, '$.ingredients') category
cross apply openjson(category.[value], '$') ingredient
This gives me
CategoryName IngredientName IngredientAmount IngredientUnit
------------ ---------------------- ---------------- --------------
malt Maris Otter Extra Pale 3.3 kilograms
malt Caramalt 0.2 kilograms
malt Munich 0.4 kilograms
hops Fuggles 25 grams
hops First Gold 25 grams
hops Fuggles 37.5 grams
hops First Gold 37.5 grams
hops Cascade 37.5 grams
To get the data from the second object in the JSON ("id": 2), the invalid JSON from the question must be corrected to be valid first. It is missing the square array brackets [].
This turns the invalid JSON
{
"id": 1,
...
},
{
"id": 2,
...
}
into a valid JSON
[
{
"id": 1,
...
},
{
"id": 2,
...
}
]
To parse the malt data from all objects, you can then use the query below:
select json_value(item.[value], '$.name') as ItemName,
json_value(ingredient.[value], '$.name') as MaltName,
json_value(ingredient.[value], '$.amount.value') as MaltAmount,
json_value(ingredient.[value], '$.amount.unit') as MaltUnit
from openjson(#json2, '$') item
cross apply openjson(item.[value], '$.ingredients.malt') ingredient;
Resulting in
ItemName MaltName MaltAmount MaltUnit
------------- ---------------------- ---------- ---------
Buzz Maris Otter Extra Pale 3.3 kilograms
Buzz Caramalt 0.2 kilograms
Buzz Munich 0.4 kilograms
Trashy Blonde Maris Otter Extra Pale 3.25 kilograms
Trashy Blonde Caramalt 0.2 kilograms
Trashy Blonde Munich 0.4 kilograms

QuickBooks Online API - Sales tax: trying to override automatic sales tax, but it's not respecting my inputs

QBOnline API: Problems with overriding Sales Tax - passed in values not respected on Invoice (AST seems to override my override)
Hi all,
Yes, I'm new to the API. Diving in head first! :)
I'm trying to troubleshoot a problem with my app. It's submitting new Invoices to the QBOnline API, and passing in all the line item information. We aren't trying to use the AST system - so I was trying to set the override values. (Documentation: https://developer.intuit.com/app/developer/qbo/docs/workflows/manage-sales-tax-for-us-locales#specifying-sales-tax)
I think that I am providing the values correctly, but the system is still calculating it's own sales tax amount.
Target API URL:
https://sandbox-quickbooks.api.intuit.com/v3/company/4620816365232674520/Invoice?minorversion=4
Here's an example of the object we are passing in (edited for privacy, expanded for human readability):
{
"BillAddr": {
"City": "AnywhereVille",
"Country": "USA",
"CountrySubDivisionCode": "CA",
"Line1": "",
"Line2": "Customer Name LLP",
"Line3": "45 E. Pacific Coast Highway",
"Line4": "",
"PostalCode": "90804"
},
"CustomField": [{
"DefinitionId": "1",
"Name": "P.O. Number",
"StringValue": "",
"Type": "StringType"
}, {
"DefinitionId": "2",
"Name": "Sales Rep",
"StringValue": "TY",
"Type": "StringType"
}],
"CustomerMemo": {
"value": "Pay your invoice online at: ..."
},
"CustomerRef": {
"value": "60"
},
"DocNumber": "11111",
"DueDate": "2022-08-01",
"Line": [{
"Amount": 576,
"Description": "",
"DetailType": "SalesItemLineDetail",
"LineNum": 1,
"SalesItemLineDetail": {
"ClassRef": {
"name": "3 Studios",
"value": "1111111111111"
},
"ItemRef": {
"name": "16GBUSBFLASH",
"value": "21"
},
"Qty": 48,
"TaxCodeRef": {
"value": "TAX"
},
"UnitPrice": 12
}
}, {
"Amount": 168,
"Description": "",
"DetailType": "SalesItemLineDetail",
"LineNum": 4,
"SalesItemLineDetail": {
"ClassRef": {
"name": "3 Studios",
"value": "1111111111111"
},
"ItemRef": {
"name": "DVDMEDIA",
"value": "23"
},
"Qty": 12,
"TaxCodeRef": {
"value": "TAX"
},
"UnitPrice": 14
}
}, {
"Amount": 600,
"Description": "",
"DetailType": "SalesItemLineDetail",
"LineNum": 6,
"SalesItemLineDetail": {
"ClassRef": {
"name": "3 Studios",
"value": "1111111111111"
},
"ItemRef": {
"name": "DIGITALDEL30",
"value": "25"
},
"Qty": 12,
"TaxCodeRef": {
"value": "TAX"
},
"UnitPrice": 50
}
}],
"SalesTermRef": {
"value": "8"
},
"TxnDate": "2022-08-01",
"TxnTaxDetail": {
"TaxLine": [{
"Amount": 130.98,
"DetailType": "TaxLineDetail",
"TaxLineDetail": {
"NetAmountTaxable": 576,
"PercentBased": true,
"TaxPercent": 0.0925,
"TaxRateRef": {
"value": "4"
}
}
}],
"TotalTax": 130.98,
"TxnTaxCodeRef": {
"value": "4"
}
}
}

Convert TEXT file to JSON in Dataweave

How can I convert TEXT file into JSON in Dataweave.
Input:
"[
{
"quantity": "1.0",
"uom": "every",
"amount": "5.0",
"allocation": [
{
"ID": "22245",
"price": "156",
"desc": "Product A1"
}
],
},
{
"quantity": "2.0",
"uom": "all",
"amount": "65888",
"allocation": [
{
"ID": "65665",
"price": "7789",
"desc": "Product A2"
}
],
}
]"
Expected output in JSON:
ex:
[
{
"quantity": "1.0",
"uom": "every",
"amount": "5.0",
"allocation": [
{
"ID": "22245",
"price": "156",
"desc": "Product A1"
}
],
},
{
"quantity": "2.0",
"uom": "all",
"amount": "65888",
"allocation": [
{
"ID": "65665",
"price": "7789",
"desc": "Product A2"
}
],
}
]
enter image description here
Use the read() function to parse the input string as JSON.
{
ex: read(payload,"application/JSON")
}

Don't serialize fields if it exists further up the tree

Lots of my domain objects have a unit field. Currently when I serialize, every object outputs the unit field:
{
"projects": [
{
"name": "project1",
"unit": "mm",
"size": {
"x": {
"value": 100,
"unit": "mm"
},
"y": {
"value": 120,
"unit": "mm"
},
"z": {
"value": 80,
"unit": "mm"
}
},
"materials": [
{
"type": "wood",
"unit": "mm",
"size": {
"x": {
"value": 100,
"unit": "mm"
},
"y": {
"value": 120,
"unit": "mm"
},
"z": {
"value": 80,
"unit": "mm"
}
}
},
...
]
},
...
]
}
I need the individual objects to have the unit property, because they are sometimes serialized on their own. e.g.
{
"length": {
"value": 100,
"unit": "mm"
}
}
But when it is nested in another object that already specifies the unit, it leads to an awful lot of duplication.
How can I get jackson to omit the unit field if there is already a unit field with the same value somewhere further up the tree?
Here are some example outputs I want to achieve using the same instances but just changing what the root object of the serialization is:
{
"projects": [
{
"name": "project1",
"unit": "mm",
"size": {
"x": {
"value": 100
},
"y": {
"value": 120
},
"z": {
"value": 80
}
},
"materials": [
{
"type": "wood",
"size": {
"x": {
"value": 100
},
"y": {
"value": 120
},
"z": {
"value": 80
}
}
},
...
]
},
...
]
}
{
"type": "wood",
"unit": "mm",
"size": {
"x": {
"value": 100
},
"y": {
"value": 120
},
"z": {
"value": 80
}
}
}
{
"value": 100,
"unit": "mm"
}
AFAIK you can’t add business logic like that in the parser.
You’ll have to massage the data class before the mapper.

How to group an array by Month and Year React Native

i have an json response like this :
{
"result": [
{
"id": 2984,
"amount": 5000000,
"account": "money",
"trade_no": "2121683414670617655",
"type": 0,
"for_id": 0,
"created_at": "2021-02-16 20:50:14",
"payment_method": 0,
"status": 1
},
{
"id": 2999,
"amount": -450000,
"account": "money",
"trade_no": "212173166272246C118",
"type": 5,
"for_id": "2021021760479",
"created_at": "2021-01-17 10:14:22",
"payment_method": "0",
"status": 1
},
],
"code": 200,
"description": "OK"
}
and then i want grouping the api by month and year look like this :
please help me to solve this
You can use groupBy method from lodash.
var grouped = _.groupBy(result, function(item) {
return item.created_at.substring(0,7);
});
This will give you data in below format:
{
"2021-02": [
{
"id": 2984,
"amount": 5000000,
"account": "money",
"trade_no": "2121683414670617655",
"type": 0,
"for_id": 0,
"created_at": "2021-02-16 20:50:14",
"payment_method": 0,
"status": 1
}
],
"2021-01": [
{
"id": 2999,
"amount": -450000,
"account": "money",
"trade_no": "212173166272246C118",
"type": 5,
"for_id": "2021021760479",
"created_at": "2021-01-17 10:14:22",
"payment_method": "0",
"status": 1
}
]
}
When rendering the data, you can format your Year-Month key to string using moment like this,
moment('2021-01', 'YYYY-MM').format('MMMM YYYY');