I need some help Parsing JSON Array in Azure APIM policy. My input to my API is a JSON Array but I want the output of the API to be JSON without the "[]", I just can't get the policy to remove them.
Here is the JSON Array input to my API:
[
{
"id": "myId",
"topic": "myTopic",
"subject": "/apis/test;Rev=1",
"data": {
"resourceUri": "myResourceUri"
},
"eventType": "Microsoft.ApiManagement.APIUpdated",
"dataVersion": "1",
"metadataVersion": "1",
"eventTime": "2022-09-08T14:22:46.7708654Z"
}
]
But I would like the Output of the policy to remove the square brackets [], like this:
{
"id": "myId",
"topic": "myTopic",
"subject": "/apis/test;Rev=1",
"data": {
"resourceUri": "myResourceUri"
},
"eventType": "Microsoft.ApiManagement.APIUpdated",
"dataVersion": "1",
"metadataVersion": "1",
"eventTime": "2022-09-08T14:22:46.7708654Z"
}
Here is my policy (which I copied from a tutorial and manipulated):
<policies>
<inbound>
<base />
<set-variable value="#(context.Request.Headers["Aeg-Event-Type"].Contains("SubscriptionValidation"))" name="isEventGridSubscriptionValidation" />
<set-variable value="#(context.Request.Headers["Aeg-Event-Type"].Contains("Notification"))" name="isEventGridNotification" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault<bool>("isEventGridSubscriptionValidation"))">
<return-response>
<set-status code="200" reason="OK" />
<set-body>#{
var events = context.Request.Body.As<string>();
JArray a = JArray.Parse(events);
var eventGridData = a.First["data"];
var validationCode = eventGridData["validationCode"];
var jOutput =
new JObject(
new JProperty("validationResponse", validationCode)
);
return jOutput.ToString();
}</set-body>
</return-response>
</when>
<when condition="#(context.Variables.GetValueOrDefault<bool>("isEventGridNotification"))">
<send-one-way-request mode="new">
<set-url>https://hooks.slack.com/services/mySlackHandle</set-url>
<set-method>POST</set-method>
<set-body>#{
var events = context.Request.Body.As<string>();
JArray a = JArray.Parse(events);
var eventGridData = a.First["data"];
var song = eventGridData["song"];
return new JObject(
new JProperty("text", String.Format(" {1}",
song, a))).ToString();
}</set-body>
</send-one-way-request>
<return-response>
<set-status code="200" reason="OK" />
</return-response>
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
ANY HELP GREATLY APPRECIATED! Extra points if you can remove the reference to "song", which I cannot without breaking the policy...
You have to convert your json array to a json object like this:
<set-body>#(Newtonsoft.Json.JsonConvert.SerializeObject((JObject)(context.Request.Body.As<JArray>(preserveContent: true))[0]))</set-body>
Your can remove the serialization if you want to keep the body as a json object. But the trick is to capture the body as a JArray and then use the [0] to choose the "first" object in the array and thereafter convert your body to a JObject.
Related
output of the json not formatted in APIM developer portal and HTTP response gives as follows also
HTTP/1.1 200 OK
api-supported-versions: 1.0,2.0
content-length: 1979
content-type: application/json; charset=utf-8
date: Tue, 06 Sep 2022 11:50:38 GMT
strict-transport-security: max-age=15552001; includeSubDomains; preload
in above it say content-type: application/json but out put as below (in the developer portal responses section content type also "application/json" )
{
"totalRowCount": 1,
"data": [
{
"Code": "",
"prCode": "CW1208",
"Name": "CW1208 Quarterly Milestones",
"description": "",
"CategoryCode": "Efficiency",
"dataSource": "",
"custodian": "",
"TimeframeCode": "Lag",
"TypeName": "Single",
"active": true,
"InputTypeCode": "Input",
"rOfficer": "Stephen MCKAY",
"rOfficerCode": "Stephen ",
"AggregationMethodCode": "Average",
"reportingPeriod": "Quarter(s)",
"responsibleOfficer": "Stephen ",
"unit": "%",
"method": "MOREISBETTER",
"datefrom": "2021-04-01T00:00:00",
"dateto": "2021-06-30T00:00:00",
"target": 100.00,
"actual": 0.00,
"variance": 0.00,
"performance": 0.000000,
"trafficLight": "OFFTRACK",
"comment": "Quarterly comment.."
}
]
}
Any idea how to fix this?
This is how I added the response
in the developer portal example output
all operation policies
<set-header name="Token" exists-action="skip">
<value>#(context.Request.OriginalUrl.Query.GetValueOrDefault("Token"))</value>
</set-header>
<choose>
<!--QA-->
<when condition="#(context.Subscription.PrimaryKey.Equals("8ug4ac4a02"))">
<set-backend-service base-url="https://xxxxx.com/API/api/v1/" />
</when>
<!--Other Clients-->
<otherwise>
<set-backend-service base-url="https://xxxxemo.xxxx.com/api/v1/" />
</otherwise>
</choose>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="X-AspNet-Version" exists-action="delete" />
<set-body template="none"></set-body>
<choose>
<when condition="#(context.Response.StatusCode == 404)">
<set-status code="200" reason="No Records Found" />
<set-body template="none">{"totalRowCount":0,"data":[{"":""}]}</set-body>
</when>
</choose>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Please set the response content type to application/json.
Outbound policy:
<outbound>
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="X-AspNet-Version" exists-action="delete" />
<set-body template="none"></set-body>
<choose>
<when condition="#(context.Response.StatusCode == 404)">
<set-status code="200" reason="No Records Found" />
<!--
<set-body template="none">{"totalRowCount":0,"data":[{"":""}]}</set-body>
-->
<set-body>#{
var body = new JObject();
body["totalRowCount"] = 0;
var data = new JArray();
var dataValue = new JObject();
dataValue[""] = "";
data.Add(dataValue);
body["data"] = data;
return body.ToString();
}</set-body>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
</when>
</choose>
<base />
</outbound>
Result:
I'm trying to build an API in azure and trying to modify the incoming json request using liquid template via set body policy. But the json elements are coming as null
Incoming json request
{
"GetOrderDetailsResponse": {
"OrderId": 1,
"OrderDate": "2018-08-13",
"OrderLines": {
"OrderLine": [
{
"Product": "Pizza Margherita",
"Amount": 5
},
{
"Product": "Pizza Calzone",
"Amount": 2
},
{
"Product": "Pizza Funghi",
"Amount": 1
}
]
}
}
}
Policy code
<policies>
<inbound>
<base />
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body template="liquid">
{
"orderId" : "{{body.GetOrderDetailsResponse.OrderId}}",
"orderDate" : "{{body.GetOrderDetailsResponse.OrderDate | Date: "dd/MM/yyyy"}}",
"orderLines" : [
{% JSONArrayFor orderline in body.GetOrderDetailsResponse.OrderLines %}
{
"product" : "{{orderline.Product}}",
"amount" : "{{orderline.Amount}}"
}
{% endJSONArrayFor %}
]
}
</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<!--Create JSON Response via liquid template-->
</outbound>
<on-error>
<base />
</on-error>
</policies>
Response is blank
{
"orderId": "",
"orderDate": "",
"orderLines": []
}
I'm new to this,please advise if i'm missing something obvious
As mentioned here in the official docs, the Content-Type header must be set to application/json for the body to parsed and available to the liquid template.
In this case, you could either make sure the request sent has it set or manually set it in the inbound scope (not inside return-response) using the set-header policy
Is it possible to apply compression (gzip) to a mocked response? Or is it limited to the outbound section? I tried to apply mocking in the outbound section but then I would need to "skip" the entire <backend> section as there is no backend yet..
This policy below works and I get a nice sample json back, but it's not compressed.
<policies>
<inbound>
<base />
<set-header name="Content-Encoding" exists-action="override">
<value>gzip</value>
</set-header>
<mock-response status-code="200" content-type="application/json" />
</inbound>
<backend />
<outbound />
<on-error>
<base />
</on-error>
</policies>
I actually got it to work using <return-response> instead. So if anyone in the future stumbles upon this, here is the complete policy:
<policies>
<inbound>
<base />
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="Content-Encoding" exists-action="override">
<value>gzip</value>
</set-header>
<set-body>{
"success": true,
"data": {
"foo": [
{
"id": 1,
"bar": "xxx"
},
{
"id": 2,
"bar": "yyy"
},
{
"id": 3,
"bar": "zzz"
}
]
}
}</set-body>
</return-response>
</inbound>
<backend />
<outbound />
<on-error>
<base />
</on-error>
</policies>
The mule payload is in flowVars (infodata)
[
{
"status": "submitted",
"identity": "",
"papers": {
"code1": "12csd3cbsdj",
"code2": "skd02nd28dn",
"date": "2016-06-22",
"party": {
"due_date": "2016-06-22",
"personel": {
"email": "tt#test.com",
"value": {
"amount": "0.10",
"inflation": "HIGH"
}
}
}
}
}
]
Inside Dataweave,
(1) how to remove the square brackets?
(2) how to replace the value of amount and inflation dynamically (from flowVars)?
Question 2:You can directly use flowVars inside dataweave or if the values are in url you can dynamically set values using inboundProperties. Refer: https://docs.mulesoft.com/mule-user-guide/v/3.7/dataweave-reference-documentation
I have used set variable where you can dynamically exact it.Test Url used in this flow: http://localhost:8083/test?inflation=HIGH
<flow name="testFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP">
</http:listener>
<logger level="INFO" doc:name="Logger"/>
<set-variable variableName="dynamicValueAmount" value="#['2']" doc:name="Variable"/>
<dw:transform-message doc:name="Transform Message">
<dw:set-payload><![CDATA[%dw 1.0
%output application/json
---
[
{
"status": "submitted",
"identity": "",
"papers": {
"party": {
"due_date": "2016-06-22",
"personel": {
"email": "tt#test.com",
"value": {
"amount": flowVars.dynamicValueAmount,
"inflation": inboundProperties.'http.query.params'.inflation
}}}}}
]]]></dw:set-payload>
</dw:transform-message>
<object-to-string-transformer doc:name="Object to String"/>
<set-payload value="#[ message.payload =org.mule.util.StringUtils.replace(message.payload,"[","{");message.payload =org.mule.util.StringUtils.replace(message.payload,"]","}")]" doc:name="Set Payload"/>
<logger level="INFO" doc:name="Logger"/>
</flow>
Regarding Question one, i have externally used replace function in set Payload( Working fine - other way). I believe it can be achieve in standard way by using Dataweave itself. Let wait for the answers.
I have resolved question #1
%dw 1.0
%output application/java
---
{
data:flowVars.infodata replace /(\[|\])/ with ""
}
I'm still trying to understand how to dynamically change the content of the payload for question #2.
Looking at the sample flowVar content, it looks like an array of json object. Is that correct? If it is array then inside dataweave, you can either iterate over flowVars.infodata map {} or just get the first object data: flowVars.infodata[0].
Not sure what you really mean by dynamically changing content. #star has shown a way how you can reference any variables inside your dataweave code. May be you can add some of your code that you are looking to edit?
I have this json payload, I would want to collect all externalListingId in one shot -
{
"listings": {
"numFound": 3,
"listing": [
{
"status": "INACTIVE",
"pricePerTicket": {
"amount": 100,
"currency": "USD",
},
"paymentType": "1",
"externalListingId": "12208278",
"city": "New York"
},
{ "status": "ACTIVE",
"pricePerTicket": {
"amount": 4444,
"currency": "USD"
},
"paymentType": "1",
"externalListingId": "CID1421798897102:151681733",
"city": "Seattle"
}
]
}
}
I am using MVEL expression -
<enricher target="#[flowVars['sData']]" source="#[(externalListingId in payload.listing)]" doc:name="Message Enricher">
<json:json-to-object-transformer returnClass="java.util.HashMap" doc:name="JSON to Object"/>
</enricher>
But this is giving error !! - Message payload is of type: ReleasingInputStream.
Scenario - I would like to collect all externalListingId into flowVariable, convert to hashmap. I have another CSV file as input, would like to loop through that payload and check if this map contains the id!!
I am following this earlier post -
Extracting array from JSON in mule esb
You need to transform the message's streaming payload to an object with:
<json:json-to-object-transformer returnClass="java.lang.Object" />
before the enricher, and remove the one you have inside of it.
You also need to fix your MEL, as the listings property is missing in it:
source="#[(externalListingId in payload.listings.listing)]"
Source: http://www.mulesoft.org/documentation/display/current/Mule+Expression+Language+Tips#MuleExpressionLanguageTips-JSONProcessing
it worked with the same definition.
<flow name="test-mule-json-extractFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/json" doc:name="HTTP"/>
<enricher source="#[(externalListingId in payload.listings.listing)]" target="#[flowVars.ids]" doc:name="Message Enricher">
<json:json-to-object-transformer returnClass="java.util.HashMap" doc:name="JSON to Object"/>
</enricher>
<logger message=":::::::::::::::#[flowVars.ids]" level="INFO" doc:name="Logger"/>
</flow>
Note: the json input you mentioned is not valid due to available of an extra comma.