I have parsed a JSON string in Logic Apps using the parse json connector. How do I put the output from that step into an xml file?
One suggestion I've seen is to use "when a http request is received", but this is a trigger and I need an action.
On user voice there is a request for a parse xml function
here
. I need a workaround.
Another problem I'm having is that parse json is returning an object, but I think I need an array.
This is the schema in my parse json connector. After parse json I'm trying to initialise a variable. It's not accepting a type of string.
<code>
{
"properties": {
"PR_ADD1": {
"type": "string"
},
"PR_ADD2": {
"type": "string"
},
"PR_ADD3": {
"type": "string"
},
"RLS_GROUP": {
"type": "string"
}
},
"title": "The Root Schema",
"type": "object"
}
</code>
How do I initialize the first variable as string with the output from my
previous parse json step? In [this example]
(https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-perform-
data-operations#parse-json-action) there is only one record. My parse
json step has lots of records.
Here is some sample JSON
<code>
{
"properties": {
"PR_ADD1": {
"type": "string"
},
"PR_ADD2": {
"type": "string"
},
"PR_ADD3": {
"type": "string"
},
"PR_ZONEC": {
"type": "string"
},
"RLS_GROUP": {
"type": "string"
}
},
"title": "The Root Schema",
"type": "object"
}
</code>
Here is my Logic APP
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose_2": {
"inputs": "#length(variables('IntermediateArray'))",
"runAfter": {
"Scope": [
"Succeeded"
],
"Set_variable": [
"Succeeded",
"Skipped"
]
},
"type": "compose"
},
"Compose_From_HTTP2": {
"inputs": "#body('HTTP_2')",
"runAfter": {
"HTTP_2": [
"Succeeded"
]
},
"type": "compose"
},
"HTTP_2": {
"inputs": {
"body": {
"FORMAT": "payload",
"FROM": 0,
"GRIDID": "PROP",
"GRIDVIEW": "1",
"HITS": 100,
"ORDERBY": "PR_DATESOLD",
"PROFILE": [
{
"PR_NAME": "G*",
"PR_USER1": "GENERATED"
}
],
"sessionID": "#body('Parse_JSON3')['sessionID']"
},
"headers": {
"Accept": "text/json",
"Content-Type": "text/json"
},
"method": "POST",
"uri": "#variables('uri_DefGrid')"
},
"runAfter": {
"Parse_JSON3": [
"Succeeded"
]
},
"type": "Http"
},
"HTTP_Logout": {
"inputs": {
"body": {
"method": "logout",
"sessionID": "#body('Parse_JSON3')['sessionID']"
},
"headers": {
"Accept": "text/json",
"Content-Type": "text/json"
},
"method": "POST",
"uri": "#variables('uri_logout')"
},
"runAfter": {
"Initialize_uri_logout": [
"Succeeded"
]
},
"type": "Http"
},
"Initialize_Header": {
"inputs": {
"variables": [
{
"name": "Header",
"type": "string",
"value": "{\"Accept\":\"text/json\",\"Content-Type\":\"text/json\"}"
}
]
},
"runAfter": {
"Initialize_body_login": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_body_login": {
"inputs": {
"variables": [
{
"name": "body_login",
"type": "string",
"value": "json(#{triggerBody()})"
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_intermediateArray": {
"inputs": {
"variables": [
{
"name": "intermediateArray",
"type": "array"
}
]
},
"runAfter": {
"Compose_From_HTTP2": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_sharepointLibraryPath": {
"inputs": {
"variables": [
{
"name": "sharepointLibraryPath",
"type": "string",
"value": "https://qqqonline.sharepoint.com/teams-and-projects/dev/hhh"
}
]
},
"runAfter": {
"Compose_2": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_uri_DefGrid": {
"inputs": {
"variables": [
{
"name": "uri_DefGrid",
"type": "string",
"value": "https://mh-uat.ttt-app.uk:443/qqq_uat/wrd/run/pppMHAPI.GRIDGET"
}
]
},
"runAfter": {
"Initialize_uri_login": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_uri_login": {
"inputs": {
"variables": [
{
"name": "uri_login",
"type": "string",
"value": "https://mh-uat.ttt-app.uk:443/qqq_uat/wrd/run/pppJSONSERVICE.LOGIN"
}
]
},
"runAfter": {
"Initialize_Header": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_uri_logout": {
"inputs": {
"variables": [
{
"name": "uri_logout",
"type": "string",
"value": "https://mh-uat.ttt-app.uk:443/qqq_uat/wrd/run/pppJSONSERVICE.LOGOUT"
}
]
},
"runAfter": {
"Initialize_sharepointLibraryPath": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "initializeArray",
"type": "array",
"value": "#outputs('Compose_From_HTTP2')"
}
]
},
"runAfter": {
"Initialize_intermediateArray": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_JSON3": {
"inputs": {
"content": "#triggerBody()",
"schema": {
"properties": {
"RLS_WHERE": {
"$id": "#/properties/RLS_WHERE",
"type": "string"
},
"contact": {
"type": "string"
},
"error": {
"type": "string"
},
"errorId": {
"type": "string"
},
"fullName": {
"type": "string"
},
"labellanguage": {
"type": "string"
},
"language": {
"type": "string"
},
"message": {
"type": "string"
},
"params": {
"properties": {
"WOPARTSOPT": {
"type": "string"
}
},
"required": [
"WOPARTSOPT"
],
"title": "The Params Schema",
"type": "object"
},
"role": {
"type": "string"
},
"sessionID": {
"type": "string"
},
"success": {
"type": "string"
},
"userEmail": {
"$id": "#/properties/userEmail",
"type": "string"
}
},
"required": [
"success",
"message",
"sessionID",
"language",
"labellanguage",
"error",
"errorId",
"fullName",
"role",
"contact",
"RLS_WHERE",
"userEmail",
"params"
],
"title": "The Root Schema",
"type": "object"
}
},
"runAfter": {
"Initialize_uri_DefGrid": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Scope": {
"actions": {
"Scope_2": {
"actions": {
"Compose": {
"inputs": "#array(outputs('Compose_From_HTTP2'))",
"runAfter": {},
"type": "compose"
},
"Set_variable_2": {
"inputs": {
"name": "intermediateArray",
"value": "#outputs('Compose')"
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"runAfter": {},
"type": "Scope"
}
},
"runAfter": {
"Initialize_variable": [
"Failed"
]
},
"type": "Scope"
},
"Set_variable": {
"inputs": {
"name": "intermediateArray",
"value": "#outputs('Compose_From_HTTP2')"
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"HTTP": {
"inputs": {
"body": {
"method": "login",
"password": "qqq_API",
"username": "qqq_API"
},
"headers": {
"Accept": "text/json",
"Content-Type": "text/json"
},
"method": "POST",
"uri": "https://mh-uat.ttt-app.uk:443/qqq_uat/wrd/run/pppJSONSERVICE.LOGIN"
},
"recurrence": {
"frequency": "Minute",
"interval": 4
},
"type": "Http"
}
}
},
"parameters": {}
}
The parse json screenshot showed the top of the schema. Here is the bottom.
"RLS_GROUP": {
"type": "string"
}
},
"title": "The Root Schema",
"type": "object"
}
When I generate the schema from sample output in parse json I get error messages like this. I think that when integer fields have not value they are coming back with two double quotes to denote empty rather than null. Also, I think the schema is basing itself on the first record which could have data. Subsequent records may not. Conversely the first record may not have data for a particular field, but subsequent records will have data for that field.
"message": "Invalid type. Expected Integer but got String.",
"lineNumber": 0,
"linePosition": 0,
"path": "Payload[1].PR_RENT",
"value": "",
"schemaId": "#/properties/Payload/items/properties/PR_RENT",
"errorType": "type",
"childErrors": []
},
{
"message": "Invalid type. Expected String but got Integer.",
"lineNumber": 0,
"linePosition": 0,
"path": "Payload[3].PR_ID",
"value": 1363,
"schemaId": "#/properties/Payload/items/properties/PR_ID",
"errorType": "type",
"childErrors": []
},
{
"message": "Invalid type. Expected String but got Integer.",
"lineNumber": 0,
"linePosition": 0,
"path": "Payload[3].PR_USER8",
"value": 0,
"schemaId": "#/properties/Payload/items/properties/PR_USER8",
"errorType": "type",
"childErrors": []
}
1. For your first question, we can use xml() method in logic app to do the convert. As I don't know your json data(you mentioned it is a json string in your question), so I assuming that your data is like the data below:
[
{
"name": "Mike",
"city": "ABC"
},
{
"name": "Tom",
"city": "DEF"
}
]
Then we can convert it by following the steps in the screenshot below:
The expression in the screenshot is:
xml(json(concat('{"root":{"person":', variables('test'), '}}')))
After that, we can get the xml data at last:
<root><person><name>Mike</name><city>ABC</city></person><person><name>Tom</name><city>DEF</city></person></root>
2. For your second question about return object or array, it depends on the data you input to the "Parse JSON" action.
If you input the data which is array shown as the screenshot below
We will get a array return from the "Parse JSON" action
If you input the data which is object shown as the screenshot below
We will get an object return from the "Parse JSON" action
Hope it would be helpful to your problems~
Update:
According to your comments, you want to choose some of the fields from the "Parse JSON" and then convert it to xml. I post an example below for your reference:
I assume that your data is like this:
{
"PR_ADD1": "aaa",
"PR_ADD2": "bbb",
"PR_ADD3": "ccc",
"PR_ZONEC": "ddd",
"RLS_GROUP": "eee"
}
And in after parse json, I assume you don't want the field "PR_ZONEC" and keep other four fields, and convert them to xml. So please refer to my logic app:
I do the "Parse JSON" action.
Then, use "Compose" action, and in "Compose" action I remove the field "PR_ZONEC", keep the other four fields.
At last, convert it to xml.
The expression is
xml(json(concat('{"root":', string(outputs('Compose')), '}')))
After these steps, I got the result I want.
The whole of the xml is
<root>
<PR_ADD1>aaa</PR_ADD1>
<PR_ADD2>bbb</PR_ADD2>
<PR_ADD3>ccc</PR_ADD3>
<RLS_GROUP>eee</RLS_GROUP>
</root>
Related
I have been struggling with this for a long time to construct an output where my [key] is separated from my array value, please help to share your expertise.
Below is the response, i have received from Azure Alert and I'm looking forward to create an output (refer Expected Output image).
{
"tables": [
{
"columns": [
{
"name": "task",
"type": "string"
},
{
"name": "environment",
"type": "string"
},
{
"name": "workspace",
"type": "string"
}
],
"name": "PrimaryResult",
"rows": [
[
"job_name_1",
"PRODUCTION",
"WORKSPACE-1",
],
[
"job_name_2",
"TEST",
"WORKSPACE-2",
]
]
}
]
}
Inside Logic App. I have parsed the json and used a lot of for each loop variations but, every single time, i get stuck in traversing the rows as its a 2d array. Even though, i traverse them, i cannot associate them with columns. Any alternative approaches will be appreciated ?
Input
Expected Output
[
{
"task": "job_name_1",
"environment": "PRODUCTION",
"workspace" : "WORKSPACE-1"
},
{
"task": "job_name_2",
"environment": "TEST",
"workspace" : "WORKSPACE-2"
}
]
I'd be lying if I said this was straight forward but you can load the below definition into your tenant and see a working version.
Note: I made the assumption that you only ever have one table in your top level array given that's what you provided in your question.
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"For_Each_Row": {
"actions": {
"Append_To_Transformed_Array": {
"inputs": {
"name": "Transformed Array",
"value": "#variables('Row Object')"
},
"runAfter": {
"For_Each_Column": [
"Succeeded"
]
},
"type": "AppendToArrayVariable"
},
"For_Each_Column": {
"actions": {
"Add_Property_To_Temp_Row_Object": {
"inputs": {
"name": "Temp Row Object",
"value": "#addProperty(variables('Row Object'), variables('Columns')?[variables('Column Index')]['name'], items('For_Each_Column'))"
},
"runAfter": {},
"type": "SetVariable"
},
"Increment_Column_Index": {
"inputs": {
"name": "Column Index",
"value": 1
},
"runAfter": {
"Set_Row_Object_From_Temp_Row_Object": [
"Succeeded"
]
},
"type": "IncrementVariable"
},
"Set_Row_Object_From_Temp_Row_Object": {
"inputs": {
"name": "Row Object",
"value": "#variables('Temp Row Object')"
},
"runAfter": {
"Add_Property_To_Temp_Row_Object": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"foreach": "#item()",
"runAfter": {
"Reset_Column_Index": [
"Succeeded"
]
},
"type": "Foreach"
},
"Reset_Column_Index": {
"inputs": {
"name": "Column Index",
"value": 0
},
"runAfter": {
"Reset_Temp_Row_Object": [
"Succeeded"
]
},
"type": "SetVariable"
},
"Reset_Row_Object": {
"inputs": {
"name": "Row Object",
"value": {}
},
"runAfter": {},
"type": "SetVariable"
},
"Reset_Temp_Row_Object": {
"inputs": {
"name": "Temp Row Object",
"value": {}
},
"runAfter": {
"Reset_Row_Object": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"foreach": "#variables('Rows')",
"runAfter": {
"Initialize_Column_Index": [
"Succeeded"
]
},
"runtimeConfiguration": {
"concurrency": {
"repetitions": 1
}
},
"type": "Foreach"
},
"Initialize_Column_Index": {
"inputs": {
"variables": [
{
"name": "Column Index",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_Temp_Row_Object": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Columns": {
"inputs": {
"variables": [
{
"name": "Columns",
"type": "array",
"value": "#variables('Data')?['tables'][0]['columns']"
}
]
},
"runAfter": {
"Initialize_Data": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Data": {
"inputs": {
"variables": [
{
"name": "Data",
"type": "object",
"value": {
"tables": [
{
"columns": [
{
"name": "task",
"type": "string"
},
{
"name": "environment",
"type": "string"
},
{
"name": "workspace",
"type": "string"
}
],
"name": "PrimaryResult",
"rows": [
[
"job_name_1",
"PRODUCTION",
"WORKSPACE-1"
],
[
"job_name_2",
"TEST",
"WORKSPACE-2"
]
]
}
]
}
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_Row_Object": {
"inputs": {
"variables": [
{
"name": "Row Object",
"type": "object"
}
]
},
"runAfter": {
"Initialize_Transformed_Array": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Rows": {
"inputs": {
"variables": [
{
"name": "Rows",
"type": "array",
"value": "#variables('Data')?['tables'][0]['rows']"
}
]
},
"runAfter": {
"Initialize_Columns": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Temp_Row_Object": {
"inputs": {
"variables": [
{
"name": "Temp Row Object",
"type": "object",
"value": {}
}
]
},
"runAfter": {
"Initialize_Row_Object": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Transformed_Array": {
"inputs": {
"variables": [
{
"name": "Transformed Array",
"type": "array"
}
]
},
"runAfter": {
"Initialize_Rows": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Result": {
"inputs": "#variables('Transformed Array')",
"runAfter": {
"For_Each_Row": [
"Succeeded"
]
},
"type": "Compose"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"method": "GET",
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
The main challenges I see with having to do this are ...
The lack of ability to self reference. Because of that, I had to use a Row Object in conjunction with a Temp Row Object when adding the property each time.
The amount of nesting is a little hard to follow but it works.
Performance will be a real burden if you have a lot of rows.
One thing to note is the outer For Each action needs to have the concurrency set to 1, if it's not, you'll run into problems.
This is the end result as you were wanting ...
In the process of moving over to Azure ecosystem fully, I'm trying to recreate a mail merge via Logic apps.
The basic idea is we get the data output via stored proc with the test data similar to:
create table dbo.People (
Name nvarchar(255),
Local char(1) null,
Earned float null,
Email nvarchar(255) null
);
insert into dbo.People values ('Bob','N',20,'Bob#Fakemail.com');
insert into dbo.People values ('Bob','Y',28,'Bob#Fakemail.com');
insert into dbo.People values ('Jess','N',25,'Jess#Fakemail.com');
insert into dbo.People values ('Jess','Y',39,'Jess#Fakemail.com');
Is this doable in logic app? Trying to avoid paying for any addons.
Tried few tests and it seems to work fine with one line per name/person. Once it brings more than one line it duplicates the emails. Is there a way to combine them all?
-- for for one line outputs (from SQL) of course it will duplicate if there is more than one. Though, not sure how to combine or process in bulk.
What would be the most efficient way of doing this?
Appreciate tips and tricks!
After reproducing from my end, I could be able to achieve this by using 2 until loops where I have defined the condition to check for distinct emails and send the required details using outlook connector.
Alternatively, you can even use foreach loop instead of until if you use a similar logic. Below is the flow of my logic app.
Initially, I have used 2 Queries. One to get the total table and other to get only the distinct emails.
In first Until loop, I have Set the Email to read the distinct emails
In the second until loop, I have used condition and if it is true, the required details will be appended to the array using the below expression.
{
"Name":"#{body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Name']}",
"Local":"#{body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Local']}",
"Earned":"#{body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Earned']}",
"Email":"#{body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Email']}"
}
Finally, I'm sending the updated variable through mail
Below is the complete Code view of my Logic App
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Execute_a_SQL_query_(V2)": {
"inputs": {
"body": {
"query": "SELECT * FROM dbo.People"
},
"host": {
"connection": {
"name": "#parameters('$connections')['sql']['connectionId']"
}
},
"method": "post",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('default'))},#{encodeURIComponent(encodeURIComponent('default'))}/query/sql"
},
"runAfter": {},
"type": "ApiConnection"
},
"Execute_a_SQL_query_(V2)_-_Distinct_Emails": {
"inputs": {
"body": {
"query": "SELECT DISTINCT Email FROM dbo.People"
},
"host": {
"connection": {
"name": "#parameters('$connections')['sql']['connectionId']"
}
},
"method": "post",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('default'))},#{encodeURIComponent(encodeURIComponent('default'))}/query/sql"
},
"runAfter": {
"Execute_a_SQL_query_(V2)": [
"Succeeded"
]
},
"type": "ApiConnection"
},
"Initialize_variable_-_Details": {
"inputs": {
"variables": [
{
"name": "Details",
"type": "array"
}
]
},
"runAfter": {
"Initialize_variable_-_Email": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_-_Email": {
"inputs": {
"variables": [
{
"name": "Email",
"type": "string"
}
]
},
"runAfter": {
"Initialize_variable_-_Loop2": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_-_Loop1": {
"inputs": {
"variables": [
{
"name": "Loop1",
"type": "integer",
"value": 0
}
]
},
"runAfter": {
"Execute_a_SQL_query_(V2)_-_Distinct_Emails": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_-_Loop2": {
"inputs": {
"variables": [
{
"name": "Loop2",
"type": "integer",
"value": 0
}
]
},
"runAfter": {
"Initialize_variable_-_Loop1": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Until_-_Loop1": {
"actions": {
"Compose": {
"inputs": "#variables('Details')",
"runAfter": {
"Increment_variable_-_Loop1": [
"Succeeded"
]
},
"type": "Compose"
},
"Increment_variable_-_Loop1": {
"inputs": {
"name": "Loop1",
"value": 1
},
"runAfter": {
"Until_-_Loop2": [
"Succeeded"
]
},
"type": "IncrementVariable"
},
"Set_variable_Details_to_null": {
"inputs": {
"name": "Details",
"value": "#null"
},
"runAfter": {
"Set_variable_Loop2_to_0": [
"Succeeded"
]
},
"type": "SetVariable"
},
"Set_variable_Distinct_Email": {
"inputs": {
"name": "Email",
"value": "#{body('Execute_a_SQL_query_(V2)_-_Distinct_Emails')?['resultsets']?['Table1'][variables('Loop1')]['Email']}"
},
"runAfter": {},
"type": "SetVariable"
},
"Set_variable_Loop2_to_0": {
"inputs": {
"name": "Loop2",
"value": 0
},
"runAfter": {
"Set_variable_Distinct_Email": [
"Succeeded"
]
},
"type": "SetVariable"
},
"Until_-_Loop2": {
"actions": {
"Condition": {
"actions": {
"Append_to_array_variable": {
"inputs": {
"name": "Details",
"value": {
"Earned": "#{body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Earned']}",
"Email": "#{body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Email']}",
"Local": "#{body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Local']}",
"Name": "#{body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Name']}"
}
},
"runAfter": {},
"type": "AppendToArrayVariable"
}
},
"expression": {
"and": [
{
"equals": [
"#variables('Email')",
"#body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'][variables('Loop2')]['Email']"
]
}
]
},
"runAfter": {},
"type": "If"
},
"Increment_variable": {
"inputs": {
"name": "Loop2",
"value": 1
},
"runAfter": {
"Condition": [
"Succeeded"
]
},
"type": "IncrementVariable"
}
},
"expression": "#equals(variables('Loop2'), length(array(body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1'])))",
"limit": {
"count": 60,
"timeout": "PT1H"
},
"runAfter": {
"Set_variable_Details_to_null": [
"Succeeded"
]
},
"type": "Until"
}
},
"expression": "#equals(variables('Loop1'), length(array(body('Execute_a_SQL_query_(V2)_-_Distinct_Emails')?['resultsets']?['Table1'])))",
"limit": {
"count": 60,
"timeout": "PT1H"
},
"runAfter": {
"Initialize_variable_-_Details": [
"Succeeded"
]
},
"type": "Until"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {
"$connections": {
"value": {
"sql": {
"connectionId": "/subscriptions/<ID>/resourceGroups/<RG>/providers/Microsoft.Web/connections/sql",
"connectionName": "sql",
"id": "/subscriptions/<ID>/providers/Microsoft.Web/locations/westus2/managedApis/sql"
}
}
}
}
}
I'm trying to get a list of reviews from an api.
This gives me a JSON with nested properties.
I manage to get almost all the data I need except from the nested array ["value"]["answers"]["value"] because sometimes the array ["answers"] has one item (with "id" being either "goods", "delivery" or "service"), or 2 or all 3 of them.
So I can't use the index to get what I want.
I need to be able to check the "id" and from that append the "value" to my array.
Is there a way to write an expression that checks the "id" and from then on writes the "value" in my array ?
JSON :
...
"surveyData": [
{
"questionRef": "q1",
"type": "CORE_STAR_RATING",
"properties": {
"title": "How do you evaluate ...?"
},
"value": {
"stars": 5,
"notAnswered": false
},
"userDefinedId": "q1"
},
"userDefinedId": "q2-good"
},
{
"questionRef": "q3",
"type": "DIMENSIONS",
"properties": {
"title": "How do you evaluate on those criterias ?"
},
**"value": {
"answers": [
{
"id": "delivery",
"value": 4,
"userDefinedId": "delivery",
"name": "Delivery",
"firstLabel": "Nul",
"lastLabel": "Good"
},
{
"id": "goods",
"value": 5,
"userDefinedId": "goods",
"name": "Goods",
"firstLabel": "Nul",
"lastLabel": "Good"
},
{
"id": "service",
"value": 5,
"userDefinedId": "service",
"name": "Service",
"firstLabel": "Nul",
"lastLabel": "Good"
}
]**
},
"userDefinedId": "q3-group"
}
]
...
My array so far :
"value": {
"Event": "#items('For_each-Service')?['event']?['type']",
"Delivery": "", <= ??
"Goods": "", <= ??
"Service": "#items('For_each-Service')?['surveyData'][2]?['value']?['answers'][2]?['value']", <= doesn't work
"comment": "#items('For_each-Service')?['reply']?['comment']",
"order date": "#items('For_each-Service')?['transaction']?['date']",
"date": "#items('For_each-Service')?['reply']?['createdAt']",
"email": "#items('For_each-Service')?['customer']?['email']",
"id review": "#items('For_each-Service')?['event']?['id']",
"name client": "#items('For_each-Service')?['customer']?['fullName']",
"status": "#items('For_each-Service')?['state']",
"title": "#items('For_each-Service')?['title']"
}
Thank you
There are a couple of ways to skin this cat, some more efficient than others.
Personally, I would take the following approach.
I created a flow where, basically, the first thing it does is initialise a variable that contains your Answers array as the data ...
The next step is to initialize another variable that stores that array as XML ...
This is the expression contained within ...
xml(json(concat('{ "root": { "answer": ', variables('Answers Array'), '}}')))
In the next step, I'm basically doing what you're doing by constructing an object, only this time, the selection for the values are a little more complex, this is the definition of the body ...
{
"Delivery": #{first(xpath(xml(variables('XML')), '//id[contains(text(), "delivery")]/parent::answer/value/text()'))},
"Goods": #{first(xpath(xml(variables('XML')), '//id[contains(text(), "goods")]/parent::answer/value/text()'))},
"Service": #{first(xpath(xml(variables('XML')), '//id[contains(text(), "service")]/parent::answer/value/text()'))}
}
Using the xpath approach for querying the dataset can cut down the amount of steps you need to take to get the desired result.
If an element is missing, it will simply come back as null ...
Below is one of the workarounds to achieve your requirement. Firstly, I have initialised 3 variables for Delivery, Goods and Service Counts.
Then used Parse_JSON to extract the inner values of the JSON provided.
In the next step I'm trying to iterate through Id's using Switch action with body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['id'] expression and set the variables using below expression inside an until loop.
body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['value']
and finally you can add in your array like below
RESULTS:
You can use below code view to reproduce in your logic app
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose": {
"inputs": {
"value": {
"answers": [
{
"firstLabel": "Nul",
"id": "goods",
"lastLabel": "Good",
"name": "Goods",
"userDefinedId": "goods",
"value": 4
},
{
"firstLabel": "Nul",
"id": "service",
"lastLabel": "Good",
"name": "Service",
"userDefinedId": "service",
"value": 5
}
]
}
},
"runAfter": {
"Initialize_variable_Service_Count": [
"Succeeded"
]
},
"type": "Compose"
},
"Compose_2": {
"inputs": " \"value\": {\n \"Event\": \"#items('For_each-Service')?['event']?['type']\",\n \"Delivery\": \"#{variables('Delivery Count')}\", \n \"Goods\": \"#{variables('Goods Count')}\", \n \"Service\": \"#{variables('Service Count')}\"\n }",
"runAfter": {
"Until": [
"Succeeded"
]
},
"type": "Compose"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "i",
"type": "integer",
"value": 0
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_variable_Delivery_count": {
"inputs": {
"variables": [
{
"name": "Delivery Count",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_Goods_Count": {
"inputs": {
"variables": [
{
"name": "Goods Count",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_variable_Delivery_count": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_Service_Count": {
"inputs": {
"variables": [
{
"name": "Service Count",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_variable_Goods_Count": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": "#outputs('Compose')",
"schema": {
"properties": {
"value": {
"properties": {
"answers": {
"items": {
"properties": {
"firstLabel": {
"type": "string"
},
"id": {
"type": "string"
},
"lastLabel": {
"type": "string"
},
"name": {
"type": "string"
},
"userDefinedId": {
"type": "string"
},
"value": {
"type": "integer"
}
},
"required": [],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
},
"type": "object"
}
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Until": {
"actions": {
"Increment_variable": {
"inputs": {
"name": "i",
"value": 1
},
"runAfter": {
"Switch": [
"Succeeded"
]
},
"type": "IncrementVariable"
},
"Switch": {
"cases": {
"Case_Delivery": {
"actions": {
"Set_variable_Delivery_Count": {
"inputs": {
"name": "Delivery Count",
"value": "#body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['value']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"case": "delivery"
},
"Case_Goods": {
"actions": {
"Set_variable_Goods_Count": {
"inputs": {
"name": "Goods Count",
"value": "#body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['value']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"case": "goods"
},
"Case_Service": {
"actions": {
"Set_variable_Service_Count": {
"inputs": {
"name": "Service Count",
"value": "#body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['value']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"case": "service"
}
},
"default": {
"actions": {}
},
"expression": "#body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['id']",
"runAfter": {},
"type": "Switch"
}
},
"expression": "#equals(variables('i'), length(body('Parse_JSON')?['value']?['answers']))",
"limit": {
"count": 10,
"timeout": "PT1H"
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "Until"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
I Want to have an Azure Logic App that receives HTTP data from multiple device types.
Each device has different body JSON schema structure.
Base on the response body (structure or specific value) I want to execute specific Liquid Map so that the result will be normalized response.
(Assume that I cannot check on HTTP headers...)
Is there a better pattern than Logic App?
Thanks!
You can create a basic json schema with atleast one field that could identify where the call came from (caller should send that field in the body) and based on that field parse the incoming json against a more comprehensive schema (format 1, 2 etc).
However, I would recommend creating separate logic apps if there is a major diff b/w input posted from the sources. It is not a good practice to have different flows based on the caller. Logic app should have minimal logic. Your approach might be not be good in the long term.
Here is one of the workaround that you can try.
Here is my logic app
I have considered a sample data to explain this in a better way.
Here is the data that I'm sending to the HTTP Trigger:-
{
"devices": [
{
"device_number": 1,
"device_type": "mobile",
"device_name": "MobileA"
},
{
"device_number": 2,
"device_type": "desktop",
"device_name": "DesktopA"
},
{
"device_number": 3,
"device_type": "tablet",
"device_name": "TabletA"
},
{
"device_number": 4,
"device_type": "desktop",
"device_name": "DesktopB"
}
]
}
Then I'm trying to parse the whole data that I'm sending for future use and performing some functions which segregates the json into device_number, device_type and device_name.
Here is the schema that I'm using to parse that data that is sent to the Http Trigger
{
"type": "object",
"properties": {
"devices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"device_number": {
"type": "integer"
},
"device_type": {
"type": "string"
},
"device_name": {
"type": "string"
}
},
"required": [
"device_number",
"device_type",
"device_name"
]
}
}
}
}
In the next step I'm parsing the Data for your understanding but this is completely avoidable when you are storing the whole data into arrays
For combining or normalizing the results we can either use a compose connector and save to storage account else we can also directly send the same data directly using cosmos db Create or update document connector.
Here is the codeview of my logic app:-
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose": {
"inputs": {
"DevicesInformation": [
{
"DeviceNames": "#{variables('DeviceName')}",
"DeviceNumbers": "#{variables('DeviceNumber')}",
"DeviceTypes": "#{variables('DeviceType')}"
}
]
},
"runAfter": {
"Parse_DeviceName": [
"Succeeded"
],
"Parse_DeviceNumber": [
"Succeeded"
],
"Parse_DeviceType": [
"Succeeded"
]
},
"type": "Compose"
},
"Create_or_update_document_(V3)": {
"inputs": {
"body": {
"DevicesInformation": [
{
"DeviceNames": "#{variables('DeviceName')}",
"DeviceNumbers": "#{variables('DeviceNumber')}",
"DeviceTypes": "#{variables('DeviceType')}"
}
],
"id": "#{guid()}"
},
"host": {
"connection": {
"name": "#parameters('$connections')['documentdb_1']['connectionId']"
}
},
"method": "post",
"path": "/v2/cosmosdb/#{encodeURIComponent('AccountNameFromSettings')}/dbs/#{encodeURIComponent('container2408')}/colls/#{encodeURIComponent('#devices')}/docs"
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "ApiConnection"
},
"For_each": {
"actions": {
"Append_to_DeviceNumber_variable": {
"inputs": {
"name": "DeviceNumber",
"value": "#items('For_each')?['device_number']"
},
"runAfter": {},
"type": "AppendToArrayVariable"
}
},
"foreach": "#body('Parse_Data')?['devices']",
"runAfter": {
"Initialize_variable_DeviceNumber": [
"Succeeded"
]
},
"type": "Foreach"
},
"For_each_2": {
"actions": {
"Append_to_DeviceType_variable": {
"inputs": {
"name": "DeviceType",
"value": "#items('For_each_2')?['device_type']"
},
"runAfter": {},
"type": "AppendToArrayVariable"
}
},
"foreach": "#body('Parse_Data')?['devices']",
"runAfter": {
"Initialize_variable_DeviceType": [
"Succeeded"
]
},
"type": "Foreach"
},
"For_each_3": {
"actions": {
"Append_to_DeviceName_variable": {
"inputs": {
"name": "DeviceName",
"value": "#items('For_each_3')?['device_name']"
},
"runAfter": {},
"type": "AppendToArrayVariable"
}
},
"foreach": "#body('Parse_Data')?['devices']",
"runAfter": {
"Initialize_variable_DeviceName": [
"Succeeded"
]
},
"type": "Foreach"
},
"Initialize_variable_DeviceName": {
"inputs": {
"variables": [
{
"name": "DeviceName",
"type": "array"
}
]
},
"runAfter": {
"Parse_Data": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_DeviceNumber": {
"inputs": {
"variables": [
{
"name": "DeviceNumber",
"type": "array"
}
]
},
"runAfter": {
"Parse_Data": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_DeviceType": {
"inputs": {
"variables": [
{
"name": "DeviceType",
"type": "array"
}
]
},
"runAfter": {
"Parse_Data": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_Data": {
"inputs": {
"content": "#triggerBody()",
"schema": {
"properties": {
"devices": {
"items": {
"properties": {
"device_name": {
"type": "string"
},
"device_number": {
"type": "integer"
},
"device_type": {
"type": "string"
}
},
"required": [
"device_number",
"device_type",
"device_name"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
},
"runAfter": {},
"type": "ParseJson"
},
"Parse_DeviceName": {
"inputs": {
"content": "#variables('DeviceName')",
"schema": {
"items": {
"type": "string"
},
"type": "array"
}
},
"runAfter": {
"For_each_3": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Parse_DeviceNumber": {
"inputs": {
"content": "#variables('DeviceNumber')",
"schema": {
"items": {
"type": "integer"
},
"type": "array"
}
},
"runAfter": {
"For_each": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Parse_DeviceType": {
"inputs": {
"content": "#variables('DeviceType')",
"schema": {
"items": {
"type": "string"
},
"type": "array"
}
},
"runAfter": {
"For_each_2": [
"Succeeded"
]
},
"type": "ParseJson"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {
"$connections": {
"value": {
"documentdb_1": {
"connectionId": "/subscriptions/<YOUR_SUBSCRIPTION_ID>/resourceGroups/<YOUR_RESOURCE_GROUP>/providers/Microsoft.Web/connections/documentdb-1",
"connectionName": "documentdb-1",
"id": "/subscriptions/<YOUR_SUBSCRIPTION_ID>/providers/Microsoft.Web/locations/northcentralus/managedApis/documentdb"
}
}
}
}
}
I'm creating an Azure Logic App to email a CSV file with data. When there is no data, the Send_an_email_(V2) step is failing and I see the following error in its output:
Parameter 'Attachment Content' cannot be null or empty.
The Send_an_email_(V2) action in the Azure Logic App gets part of it's input from the output of a preceding Create_CSV_table action. It uses the body of the Create_CSV_table output as shown below, in order to construct ContentBytes for an email attachment:
"actions": {
"Create_CSV_table": {
"inputs": {
"format": "CSV",
"from": "#body('Parse_JSON')"
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "Table"
},
"Send_an_email_(V2)": {
"inputs": {
"body": {
"Attachments": [
{
"ContentBytes": "#{base64(body('Create_CSV_table'))}",
"Name": "report.csv"
}
],
"Body": "<p></p>",
"Subject": "My Report",
"To": "me#email.com"
},
"host": {
"connection": {
"name": "#parameters('$connections')['office365']['connectionId']"
}
},
"method": "post",
"path": "/v2/Mail"
},
"runAfter": {
"Create_CSV_table": [
"Succeeded"
]
},
"type": "ApiConnection"
}
When there is no data input into the Create_CSV_table step, the Create_CSV_table step is successful, and the raw output for the Create_CSV_table step shows an empty body as follows:
{
"body": ""
}
Raw input for the failing Send_an_email_(V2) step
{
HTTP stuff ...,
"body": {
"Attachments": [
{
"ContentBytes": "",
"Name": "report.csv"
}
],
"Body": "<p></p>",
"Subject": "Report",
"To": "me#mail.com"
}
}
Raw output for the failing Send_an_email_(V2) step
{
HTTP stuff ...,
"body": {
"status": 400,
"message": "Parameter 'Attachment Content' cannot be null or empty.\r\nclientRequestId: 887e3968-c7e9-4c35-b588-f76fd0e51545",
"error": {
"message": "Parameter 'Attachment Content' cannot be null or empty."
},
"source": "office365-ase.azconn-ase.p.azurewebsites.net"
}
}
How do I handle this? Do I need to implement my own null handling for the "ContentBytes" of the Send_an_email_(V2) input? If so, how do I do that? Or is there another way to handle this. I want an empty email to be sent when there is no CSV content.
I figured out that #{base64(body('Create_CSV_table'))} is an Azure Logic Apps expression (denoted by #), containing functions that act on the JSON for the body of the output of Create_CSV_table and that the enclosing {} results in the output of the expression being a string.
After the Last step you can send to storage account using Create blob(V2) Connection with .csv file extension and then send the same blob content using Get blob content(V2) Connection then Send an email.
Here are the screenshots of LogicApp
In outlook:
Here is the workflow
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Create_CSV_table_2": {
"inputs": {
"format": "CSV",
"from": "#variables('Array')"
},
"runAfter": {
"For_each_2": [
"Succeeded"
]
},
"type": "Table"
},
"Create_blob_(V2)": {
"inputs": {
"body": "#body('Create_CSV_table_2')",
"headers": {
"ReadFileMetadataFromServer": true
},
"host": {
"connection": {
"name": "#parameters('$connections')['azureblob']['connectionId']"
}
},
"method": "post",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('AccountNameFromSettings'))}/files",
"queries": {
"folderPath": "/ch1container2408",
"name": "TestSample1.csv",
"queryParametersSingleEncoded": true
}
},
"runAfter": {
"Create_CSV_table_2": [
"Succeeded"
]
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
},
"type": "ApiConnection"
},
"For_each_2": {
"actions": {
"Append_to_array_variable": {
"inputs": {
"name": "Array",
"value": {
"created_at": "#items('For_each_2')['created_at']",
"funds": "#items('For_each_2')['funds']",
"id": "#items('For_each_2')['id']",
"pair": "#items('For_each_2')['market']",
"price": "#items('For_each_2')['price']",
"side": "#items('For_each_2')['side']",
"volume": "#items('For_each_2')['volume']"
}
},
"runAfter": {},
"type": "AppendToArrayVariable"
}
},
"foreach": "#body('Parse_JSON')",
"runAfter": {
"Initialize_variable_2": [
"Succeeded"
]
},
"type": "Foreach"
},
"Get_blob_content_(V2)": {
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['azureblob']['connectionId']"
}
},
"method": "get",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('AccountNameFromSettings'))}/files/#{encodeURIComponent(encodeURIComponent('/<Your Container>','/<Your File>.csv'))}/content",
"queries": {
"inferContentType": true
}
},
"runAfter": {
"Create_blob_(V2)": [
"Succeeded"
]
},
"type": "ApiConnection"
},
"HTTP": {
"inputs": {
"method": "GET",
"uri": "https://api.wazirx.com/api/v2/trades?market=btcusdt"
},
"runAfter": {},
"type": "Http"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "Array",
"type": "array"
}
]
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_2": {
"inputs": {
"variables": [
{
"name": "Table",
"type": "array"
}
]
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": "#body('HTTP')",
"schema": {
"items": {
"properties": {
"created_at": {
"type": "string"
},
"funds": {
"type": "string"
},
"id": {
"type": "integer"
},
"market": {
"type": "string"
},
"price": {
"type": "string"
},
"side": {},
"volume": {
"type": "string"
}
},
"required": [
"id",
"market",
"price",
"volume",
"funds",
"created_at",
"side"
],
"type": "object"
},
"type": "array"
}
},
"runAfter": {
"HTTP": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Send_an_email_(V2)": {
"inputs": {
"body": {
"Attachments": [
{
"ContentBytes": "#{base64(body('Get_blob_content_(V2)'))}",
"Name": "#body('Create_blob_(V2)')?['Name']"
}
],
"Body": "<p>TABLE TEST</p>",
"Subject": "Test",
"To": "<To Address>"
},
"host": {
"connection": {
"name": "#parameters('$connections')['office365']['connectionId']"
}
},
"method": "post",
"path": "/v2/Mail"
},
"runAfter": {
"Get_blob_content_(V2)": [
"Succeeded"
]
},
"type": "ApiConnection"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"manual": {
"inputs": {},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {
"$connections": {
"value": {
"azureblob": {
"connectionId": "<Your ConnectionId>",
"connectionName": "azureblob",
"id": "<Id>"
},
"office365": {
"connectionId": "<Your ConnectionId>",
"connectionName": "office365",
"id": "<Your Id>"
}
}
}
}
}