XML to JSON Liquid fails in Logic APP on special Characters - json

Logic App received a XML response message.
This message is transformed from XML to JSON with a liquid template,but when special characters are in the XML response the Liquid fails...
XML input file (browser display)
<ET_KLANTEN_ALL>
<item>
<KUNNR>xxx</KUNNR>
<NAME1>Strandshop "Allaart"</NAME1>
</item>
</ET_KLANTEN_ALL>
XML encoded data
<ET_KLANTEN_ALL>
<item>
<KUNNR>xx</KUNNR>
<NAME1>Strandshop "Allaart"</NAME1>
</item>
</ET_KLANTEN_ALL>
<ET_RETURN/>
Liquid
"AfnemerNaam": "{{item.AfnemerNaam}}"
Liquid Error in Logic App...
IncorrectLiquidTransformOutputType.
I have tried using single quotes
Liquid
"AfnemerNaam": '{{item.AfnemerNaam}}'
This works for the above issue but it fails on the bellow situation
XML input bowser display
<ET_KLANTEN_ALL>
<item>
<KUNNR>0000071719</KUNNR>
<NAME1>*STK*Camping 't Strandheem</NAME1>
</item>
</ET_KLANTEN_ALL>
<ET_KLANTEN_ALL>
<item>
<KUNNR>Y</KUNNR>
<NAME1>*STK*Camping 't Strandheem</NAME1>
</item>
</ET_KLANTEN_ALL>
Could use some advise on this....
Required output in JSON
{
"Afnemers": [
{
"AfnemerNummer": "0000036082",
"AfnemerNaam": "Strandshop \"Allaart\"",
}
]
}
{
"Afnemers": [
{
"AfnemerNummer": "0000071719",
"AfnemerNaam": "*STK*Camping 't Strandheem"
}
]
}

Just a suggestion but if you want to avoid using liquid, you can perform a simple conversion to JSON using an expression and then loop through the result and convert it to your new structure with the loop.
Using the above, I loaded your XML (with the two items) into a string variable and then in the next step, used a Parse json step to turn it into an intermediary JSON structure that preserves the special characters, etc. like you want.
The expression above is ...
json(xml(variables('XML')))
The schema of your Parse json step (to make it easier for you) is ...
{
"properties": {
"ET_KLANTEN_ALL": {
"properties": {
"item": {
"items": {
"properties": {
"KUNNR": {
"type": "string"
},
"NAME1": {
"type": "string"
}
},
"required": [
"KUNNR",
"NAME1"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
},
"type": "object"
}
That will create this JSON ...
{
"ET_KLANTEN_ALL": {
"item": [
{
"KUNNR": "0000071719",
"NAME1": "*STK*Camping 't Strandheem"
},
{
"KUNNR": "xxx",
"NAME1": "Strandshop \"Allaart\""
}
]
}
}
Then all you need to do is loop through each item and append it to an array variable ...
... and finalise the result.
This is the end game ...!
This is the json definition of my answer if you want to load it into your own tenant.
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"For_each": {
"actions": {
"Append_to_Result": {
"inputs": {
"name": "Afnemers",
"value": {
"AfnemerNaam": "#{items('For_each')?['NAME1']}",
"AfnemerNummer": "#{items('For_each')?['KUNNR']}"
}
},
"runAfter": {},
"type": "AppendToArrayVariable"
}
},
"foreach": "#body('Parse_JSON')?['ET_KLANTEN_ALL']?['item']",
"runAfter": {
"Initialize_Array": [
"Succeeded"
]
},
"type": "Foreach"
},
"Initialize_Array": {
"inputs": {
"variables": [
{
"name": "Afnemers",
"type": "array"
}
]
},
"runAfter": {
"Initialize_Result": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Result": {
"inputs": {
"variables": [
{
"name": "Result",
"type": "object"
}
]
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_XML": {
"inputs": {
"variables": [
{
"name": "XML",
"type": "string",
"value": "<ET_KLANTEN_ALL>\n <item>\n <KUNNR>0000071719</KUNNR>\n <NAME1>*STK*Camping 't Strandheem</NAME1>\n </item>\n <item>\n <KUNNR>xxx</KUNNR>\n <NAME1>Strandshop \"Allaart\"</NAME1>\n </item>\n</ET_KLANTEN_ALL>"
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": "#json(xml(variables('XML')))",
"schema": {
"properties": {
"ET_KLANTEN_ALL": {
"properties": {
"item": {
"items": {
"properties": {
"KUNNR": {
"type": "string"
},
"NAME1": {
"type": "string"
}
},
"required": [
"KUNNR",
"NAME1"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
},
"type": "object"
}
},
"runAfter": {
"Initialize_XML": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Set_Result": {
"inputs": {
"name": "Result",
"value": {
"Afnemers": "#variables('Afnemers')"
}
},
"runAfter": {
"For_each": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"Recurrence": {
"evaluatedRecurrence": {
"frequency": "Month",
"interval": 12
},
"recurrence": {
"frequency": "Month",
"interval": 12
},
"type": "Recurrence"
}
}
},
"parameters": {}
}

We have managed it using liquid.
Please note solution to present "text" correct in the JSON use a single quote.
In all other situations use the double quote.
{% if item.AfnemerNaam contains '"' %}
"AfnemerNaam": '{{item.AfnemerNaam}}'
{% else %}
"AfnemerNaam": "{{item.AfnemerNaam}}"
{% endif %},

Related

Parsing JSON array to individual variables in Azure logic apps

I am trying to write a logic app to parse a Json Object and Update salesforce record. I am pretty new to both Salesforce and Azure logic apps, so I am trying to figure this out. Below is my Json File
{
"ContactId": null,
"Email": "asong#uog.com",
"IsInternalUpdate": false,
"Preferences": [
{
"PrefCode": "EmailOptIn",
"CurrentValue": "Yes",
"Locale": "en-US"
},
{
"PrefCode": "MobilePhone",
"CurrentValue": "1234567890",
"Locale": "en-US"
},
{
"PrefCode": "SMSOptIn",
"CurrentValue": "Yes",
"Locale": "en-US"
},
{
"PrefCode": "ProductTrends",
"CurrentValue": "ProductTrends,OffersPromotions",
"Locale": "en-US"
},
]
}
Based on email value, I need to update a custom object in Salesforce. From the preference array, Prefcode value maps to a field in Salesforce and Current value maps to field value. i.e below snippet translates to set the value for EmailOptIn field in Salesforce to "Yes"
{
"PrefCode": "EmailOptIn",
"CurrentValue": "Yes",
"Locale": "en-US"
}
So far, I was able to pass hardcoded values and successfully update salesforce record from logic app.
I am trying to set individual variables for each field, so that I can pass it directly to salesforce. I have two issues that I am running into
What is the best way to capture the field value mapping?
I have couple of fields that allow multi select, how do I set the multiselect values. Below is an example
{
"PrefCode": "ProductTrends",
"CurrentValue": "ProductTrends,OffersPromotions",
"Locale": "en-US"
}
Below is my logic app structure
1
2
What is the best way to capture the field value mapping?
I could able to achieve your requirement by constructing JSON with required details and used parse JSON step again. Below is the flow of the logic app that worked for me.
I have couple of fields that allow multi select, how do I set the multiselect values. Below is an example
I have converted the ProductTrends into array and retrieved each item using its index as below.
array(split(body('Parse_JSON_2')?['ProductTrends'],','))[0]
array(split(body('Parse_JSON_2')?['ProductTrends'],','))[1]
Alternatively, if it is possible to make things work from the target end for multiple values, you can send in array or string directly to salesforce.
Below is the complete JSON of my logic app.
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose": {
"inputs": {
"ContactId": null,
"Email": "asong#uog.com",
"IsInternalUpdate": false,
"Preferences": [
{
"CurrentValue": "Yes",
"Locale": "en-US",
"PrefCode": "EmailOptIn"
},
{
"CurrentValue": "1234567890",
"Locale": "en-US",
"PrefCode": "MobilePhone"
},
{
"CurrentValue": "Yes",
"Locale": "en-US",
"PrefCode": "SMSOptIn"
},
{
"CurrentValue": "ProductTrends,OffersPromotions",
"Locale": "en-US",
"PrefCode": "ProductTrends"
}
]
},
"runAfter": {},
"type": "Compose"
},
"Compose_2": {
"inputs": "#array(split(body('Parse_JSON_2')?['ProductTrends'],','))[0]",
"runAfter": {
"Parse_JSON_2": [
"Succeeded"
]
},
"type": "Compose"
},
"For_each": {
"actions": {
"Append_to_string_variable": {
"inputs": {
"name": "Preferences",
"value": "\"#{items('For_each')?['PrefCode']}\":\"#{items('For_each')?['CurrentValue']}\",\n"
},
"runAfter": {},
"type": "AppendToStringVariable"
}
},
"foreach": "#body('Parse_JSON')?['Preferences']",
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "Foreach"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "Preferences",
"type": "string"
}
]
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": "#outputs('Compose')",
"schema": {
"properties": {
"ContactId": {},
"Email": {
"type": "string"
},
"IsInternalUpdate": {
"type": "boolean"
},
"Preferences": {
"items": {
"properties": {
"CurrentValue": {
"type": "string"
},
"Locale": {
"type": "string"
},
"PrefCode": {
"type": "string"
}
},
"required": [
"PrefCode",
"CurrentValue",
"Locale"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Parse_JSON_2": {
"inputs": {
"content": "#concat('{',slice(variables('Preferences'),0,lastIndexOf(variables('Preferences'),',')),'}')",
"schema": {
"properties": {
"EmailOptIn": {
"type": "string"
},
"MobilePhone": {
"type": "string"
},
"ProductTrends": {
"type": "string"
},
"SMSOptIn": {
"type": "string"
}
},
"type": "object"
}
},
"runAfter": {
"For_each": [
"Succeeded"
]
},
"type": "ParseJson"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
After following the above process, I could able to get the preferences values individually.

JSON Schema with Nested Objects with different properties

The entire JSON file is rather large so I've only taken out the subsection I've had an issue with.
{
"diagrams": {
"5f759d15cd046720c28531dd": {
"_id": "5f759d15cd046720c28531dd",
"offsetX": 320,
"offsetY": 42,
"zoom": 80,
"modified": 1604279356,
"nodes": {
"5f9f5c3ccd046720c28531e4": {
"nodeID": "5f9f5c3ccd046720c28531e4",
"type": "start",
"coords": [
360,
120
],
"data": {
"name": "Start",
"color": "standard",
"ports": [
{
"type": "",
"target": "5f9f5c3ccd046720c28531e6"
}
],
"steps": []
}
},
"5f9f5c3ccd046720c28531e5": {
"nodeID": "5f9f5c3ccd046720c28531e5",
"type": "block",
"coords": [
760,
120
],
"data": {
"name": "Help Message",
"color": "standard",
"steps": [
"5f9f5c3ccd046720c28531e6",
"5f9f5c3ccd046720c28531e7"
]
}
},
"5f9f5c3ccd046720c28531e6": {
"nodeID": "5f9f5c3ccd046720c28531e6",
"type": "speak",
"data": {
"randomize": false,
"dialogs": [
{
"voice": "Alexa",
"content": "You said help. Do you want to continue?"
}
],
"ports": [
{
"type": "",
"target": "5f9f5c3ccd046720c28531e7"
}
]
}
},
"5f9f5c3ccd046720c28531e7": {
"nodeID": "5f9f5c3ccd046720c28531e7",
"type": "interaction",
"data": {
"name": "Choice",
"else": {
"type": "path",
"randomize": false,
"reprompts": []
},
"choices": [
{
"intent": "",
"mappings": []
},
{
"intent": "",
"mappings": []
}
],
"reprompt": null,
"ports": [
{
"type": "else",
"target": null
},
{
"type": "",
"target": null
},
{
"type": "",
"target": "5f9f5c3ccd046720c28531e9"
}
]
}
},
"5f9f5c3ccd046720c28531e8": {
"nodeID": "5f9f5c3ccd046720c28531e8",
"type": "block",
"coords": [
1170,
260
],
"data": {
"name": "Exit",
"color": "standard",
"steps": [
"5f9f5c3ccd046720c28531e9"
]
}
},
"5f9f5c3ccd046720c28531e9": {
"nodeID": "5f9f5c3ccd046720c28531e9",
"type": "exit",
"data": {
"ports": []
}
}
},
"children": [],
"creatorID": 42661,
"variables": [],
"name": "Help Flow",
"versionID": "5f759d15cd046720c28531db"
}
}
}
The Current JSON Schema Definition I have is:
{
"$schema":"http://json-schema.org/schema#",
"type":"object",
"properties":{
"diagrams":{
"type":"object"
}
},
"required":[
"diagrams",
]
}
The problem I am having is that within diagrams contains multiple objects with a random string as the name e.g "5f759d15cd046720c28531dd".
Then within that object there are properties such as (_id, offsetX) which I want to express as well as a nodes object, which again contains multiple objects with arbitrary names e.g ("5f9f5c3ccd046720c28531e4", "5f9f5c3ccd046720c28531e5", ...) which have a unique node definition where some nodes have different properties to other nodes (nodeID, type, data vs nodeID, type, data, coords).
My question is with all these arbitrary things such as random names as well as different properties per each node. How do I turn it into 1 JSON schema definition which covers all the cases of how a diagram/node can be made.
You can do this with additionalProperties or patternProperties.
additionalProperties applies to any property that isn't declared in properties or patternProperties.
{
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"_id": { ... },
"offsetX": { ... },
...
}
}
}
Your property names appear to always be hex numbers. If you want to enforce that those property names are always hex numbers, you can use patternProperties. Any property that matches the regex must conform to that schema.
{
"type": "object",
"patternProperties": {
"^[0-9a-f]{24}$": {
"type": "object",
"properties": {
"_id": { ... },
"offsetX": { ... },
...
}
}
},
"additionalProperties": false
}

Json schema validation for object which may have two forms

I'm trying to figure out how to validate a JSON object which may have 2 forms.
for examples
when there is no data available, the JSON could be
{
"student": {}
}
when there is data available, the JSON could be
{
"student":{
"id":"someid",
"name":"some name",
"age":15
}
}
I wrote the JSON schema in this way, but it seems not working
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://json-schema.org/draft-07/schema#",
"title": "JSON schema validation",
"properties": {
"student": {
"type": "object",
"oneOf": [
{
"required": [],
"properties": {}
},
{
"required": [
"id",
"name",
"age"
],
"properties": {
"id": {
"$id": "#/properties/student/id",
"type": [
"string",
"null"
]
},
"name": {
"$id": "#/properties/student/name",
"type": [
"string",
"null"
]
},
"age": {
"$id": "#/properties/student/age",
"type": [
"number"
]
}
}
}
]
}
}
}
I was wondering is there a way to validate it. Thank you!
An empty properties object, and an empty required object, do nothing.
JSON Schema is constraints based, in that if you don't explicitly constrain what is allowed, then it is allowed by default.
You're close, but not quite. The const keyword can take any value, and is what you want in your first item of allOf.
You can test the following schema here: https://jsonschema.dev/s/Kz1C0
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://example.com/myawesomeschema",
"title": "JSON schema validation",
"properties": {
"student": {
"type": "object",
"oneOf": [
{
"const": {}
},
{
"required": [
"id",
"name",
"age"
],
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"age": {
"type": [
"number"
]
}
}
}
]
}
}
}

JSON is not getting validated based on schema

I must be missing something here, but the below JSON is not getting validated against the schema.
For example, the required attribute from the Java/JavaScript object is never getting enforced as per the schema. (FYI- Every language object may have other attributes or nested object)
However, if I completely remove the definition and directly put under array items each separately, then it validates.
I want to use 'definitions' and gets validated.The reason I have to put all the object in definitions and later I may have to put different language object in oneOf/allOf for other certain validation check.
Online check:
Schema and JSON
JSON
{
"languages": [
{
"lang": "Java",
"trainer": "Peter"
},
{
"lang": "JavaScript",
"enrolled": "42",
"available": "5"
}
]
}
and the Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"languages"
],
"properties": {
"languages": {
"type": "array",
"minItems": 1,
"items": {
"type": "object"
},
"anyOf": [
{
"$ref": "#/definitions/Java"
},
{
"$ref": "#/definitions/JavaScript"
}
]
}
},
"definitions": {
"Java": {
"required": [
"trainer"
],
"properties": {
"lang": {
"enum": [
"Java"
]
},
"trainer": {
"type": "string"
}
}
},
"JavaScript": {
"required": [
"enrolled",
"available"
],
"properties": {
"lang": {
"enum": [
"JavaScript"
]
},
"enrolled": {
"type": "string"
},
"available": {
"type": "string"
}
}
}
}
}
The fixed schema and it works now
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"languages"
],
"properties": {
"languages": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"anyOf": [
{
"$ref": "#/definitions/Java"
},
{
"$ref": "#/definitions/JavaScript"
}
]
}
}
},
"definitions": {
"Java": {
"required": [
"trainer"
],
"properties": {
"lang": {
"enum": [
"Java"
]
},
"trainer": {
"type": "string"
}
}
},
"JavaScript": {
"required": [
"enrolled",
"available"
],
"properties": {
"lang": {
"enum": [
"JavaScript"
]
},
"enrolled": {
"type": "string"
},
"available": {
"type": "string"
}
}
}
}
}
I think that the "array" definition is not correct. The objects that can be added in the array must be refereed in the "items", after you define the type of the items. Something like this:
"languages": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"anyOf": [
{"$ref": "#/definitions/Java"},
{"$ref": "#/definitions/JavaScript"}
]
}
}
I have fixed myself
What I did: I have placed the json object blocks under items section of array.

How do parse string as JSON in Logic App?

I have JSON below which I receive from external entity. As you can see requestbody parameter is appearing as string even though it's JSON. So how do I unescape it so I can correctly parse it downstream?
{
"emailaddress": "174181#mycomp.com",
"requestbody": "{\"Id\":\"57518139-687c-4223-b08b-342f4ff426ca\",\"Properties\":{\"PrincipalId\":\"d701e7aa-5a0a-4c4a-81be-4c4b7a3967ce\",\"RoleDefinitionId\":\"/subscriptions/64ba3e4c-45e3-4d55-8132-6731cf25547f/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635\",\"Scope\":\"/subscriptions/64ba3e4c-45e3-4d55-8132-6731cf25547f\"}}"
}
Use a Parse JSON Action as shown below:
Content:
{
"emailaddress": "174181#mycomp.com",
"requestbody": "{\"Id\":\"57518139-687c-4223-b08b-342f4ff426ca\",\"Properties\":{\"PrincipalId\":\"d701e7aa-5a0a-4c4a-81be-4c4b7a3967ce\",\"RoleDefinitionId\":\"/subscriptions/64ba3e4c-45e3-4d55-8132-6731cf25547f/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635\",\"Scope\":\"/subscriptions/64ba3e4c-45e3-4d55-8132-6731cf25547f\"}}"
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"emailaddress": {
"type": "string"
},
"requestbody": {
"type": "string"
}
},
"required": [
"emailaddress",
"requestbody"
],
"type": "object"
}
Initialize Variable
-Name = Variable Name
-Type = Object
-Value = json(body('Parse_JSON')['requestbody'])
Now you can extract the properties of your Json string as shown below:
variables('jsonobj')?['Properties']
Full Code view of my sample:
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "jsonobj",
"type": "Object",
"value": "#json(body('Parse_JSON')['requestbody'])"
}
]
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": {
"emailaddress": "174181#mycomp.com",
"requestbody": "{\"Id\":\"57518139-687c-4223-b08b-342f4ff426ca\",\"Properties\":{\"PrincipalId\":\"d701e7aa-5a0a-4c4a-81be-4c4b7a3967ce\",\"RoleDefinitionId\":\"/subscriptions/64ba3e4c-45e3-4d55-8132-6731cf25547f/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635\",\"Scope\":\"/subscriptions/64ba3e4c-45e3-4d55-8132-6731cf25547f\"}}"
},
"schema": {
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"emailaddress": {
"type": "string"
},
"requestbody": {
"type": "string"
}
},
"required": [
"emailaddress",
"requestbody"
],
"type": "object"
}
},
"runAfter": {},
"type": "ParseJson"
},
"Response": {
"inputs": {
"body": "#variables('jsonobj')?['Properties']",
"statusCode": 200
},
"kind": "Http",
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "Response"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
}
}
The easiest way is to use this expression:
#json(outputs('Mock_example_data').requestbody)
Below is an example using a Compose action to mock up your data and another Compose action as a simple proof of concept.