Policy XML to JSON in Azure APIM not converting to proper JSON format - azure-api-management

i am consuming wcf service which returns XML response, in Azure APIM i am transforming those into Json response by using the below APIM policy
<xml-to-json kind="direct" apply="always" consider-accept-header="false" />
It works fine until i get xml response as bleow like contains one title in the titles section,
<TitlesResponse>
<Titles>
<Title>
<Name>BookTitle-1</Name>
<Author>BookTitle-2</Author>
</Title>
<Title>
<Name>BookTitle-1</Name>
<Author>BookTitle-2</Author>
</Title>
</Titles>
<Titles>
<Title>
<Name>BookTitle-1</Name>
</Title>
</Titles>
</TitlesResponse>
The json response for the above xml is as below
{
"TitlesResponse": {
"Titles": [
{
"Title": [
{
"Name": "BookTitle-1",
"Author": "BookTitle-2"
},
{
"Name": "BookTitle-1",
"Author": "BookTitle-2"
}
]
},
{
"Title": {
"Name": "BookTitle-1"
}
}
]
}
}
The client expects always Json array in the Title section but it's returning jsonobject when only one title is there.
I tried this in outbound set-body but not working
string xmlContent = context.Response.Body.As<string>(preserveContent: true);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlContent);
return JsonConvert.SerializeObject(doc);
Do we have any other way to convert to expected Json format from xml in the APIM

I'm not sure if there's a real solution to this problem.
It would be great if JsonConvert accepts also an XSD for serialization.
But I created a workaround:
The Request body is transformed from XML to JSON.
If the datatype is JOject, it will be replaced with an JArray:
<inbound>
<base />
<xml-to-json kind="direct" apply="always" consider-accept-header="false" />
<set-body>#{
var body = context.Request.Body.As<JObject>(true);
JArray titles = body["TitlesResponse"]["Titles"] as JArray;
for (int i = 0; i < titles.Count; i++)
{
JToken item = titles[i]["Title"];
if(item.Type == JTokenType.Object && item.Type != JTokenType.Array )
{
JObject clone = (JObject)item.DeepClone();
titles[i]["Title"] = new JArray();
((JArray)titles[i]["Title"]).Add(clone);
}
}
return body.ToString();
}</set-body>
</inbound>
Response:
{
"TitlesResponse": {
"Titles": [
{
"Title": [
{
"Name": "BookTitle-1",
"Author": "BookTitle-2"
},
{
"Name": "BookTitle-1",
"Author": "BookTitle-2"
}
]
},
{
"Title": [
{
"Name": "BookTitle-1"
}
]
}
]
}
}

Related

How we compare xml file and json in feature file in karate framework [duplicate]

I need to match and validate my JSON response with that of downstream XML response. Here are sample responses for both.
Note that Json response parameters are is not in-order with XML response.
JSON RESPONSE
"Main": {
"Cd": "ABC",
"descriptionTxt": "Sample Main",
"type": "A",
"codeType": "P",
"dt": "2018-10-15T00:00:00-05:00",
"validity": "3",
"segment": "Personal"
},
"testList": [
{
"code": "123",
"descriptionTxt": "My Description",
"categoryCd": "DUDU"
},
{
"code": "675",
"descriptionTxt": "His Description"
},
{
"code": "345",
"descriptionTxt": "Your Description",
"categoryCd": "BH"
}
]
XML RESPONSE
<S:Body>
<ns4:code>ABC </ns4:code>
<ns5:description>Sample Main</ns5:description>
<ns5:Date>2018-10-15</ns5:Date>
<ns5:Type>A</ns5:Type>
<ns5:codeType>P</ns5:codeType>
<ns5:validity>3</ns5:validity >
<ns5:Segment>PERSONAL </ns5:Segment>
<ns5:unwanted>Unwanted XML Parameter</ns5:unwanted>
<ns4:Test>
<ns5:code>123 </ns5:code>
<ns5:description>My Description</ns5:description>
<ns5:categoryCode>DUDU</ns5:categoryCode>
<ns5:unwanted>Unwanted XML Parameter</ns5:unwanted>
</ns4:Test>
<ns4:Test>
<ns5:code>345 </ns5:code>
<ns5:description>Your Description</ns5:description>
<ns5:categoryCode>BH</ns5:categoryCode>
</ns4:Test>
<ns4:Test>
<ns5:code>675 </ns5:code>
<ns5:description>His Description</ns5:description>
<ns5:unwanted>Unwanted XML Parameter</ns5:unwanted>
</ns4:Test>
It would have been nice if you took the time to post well-formed JSON and XML, but anyway. I'm focusing on the hard problem here, which is to map repeated XML elements to JSON, if you paste the below into a Scenario you can see it work:
* def json =
"""
{
"Main": {
"Cd":"ABC",
"descriptionTxt":"Sample Main",
"type":"A",
"codeType":"P",
"dt":"2018-10-15T00:00:00-05:00",
"validity":"3",
"segment":"Personal"
},
"testList":[
{
"code":"123",
"descriptionTxt":"My Description",
"categoryCd":"DUDU"
},
{
"code":"675",
"descriptionTxt":"His Description"
},
{
"code":"345",
"descriptionTxt":"Your Description",
"categoryCd":"BH"
}
]
}
"""
* def xml =
"""
<ns4:root xmlns:ns4="http://foo.com" xmlns:ns5="http://bar.com">
<ns4:Test>
<ns5:code>123</ns5:code>
<ns5:description>My Description</ns5:description>
<ns5:categoryCode>DUDU</ns5:categoryCode>
<ns5:unwanted>Unwanted XML Parameter</ns5:unwanted>
</ns4:Test>
<ns4:Test>
<ns5:code>345</ns5:code>
<ns5:description>Your Description</ns5:description>
<ns5:categoryCode>BH</ns5:categoryCode>
</ns4:Test>
<ns4:Test>
<ns5:code>675</ns5:code>
<ns5:description>His Description</ns5:description>
<ns5:unwanted>Unwanted XML Parameter</ns5:unwanted>
</ns4:Test>
</ns4:root>
"""
* def list = $xml/root/Test
* def xpath = function(x, p){ try { return karate.xmlPath(x, p) } catch (e) { return '#notpresent' } }
* def fun = function(x){ return { code: xpath(x, '/Test/code'), descriptionTxt: xpath(x, '/Test/description'), categoryCd: xpath(x, '/Test/categoryCode') } }
* def temp = karate.map(list, fun)
* print temp
* print json.testList
* match json.testList contains temp
Mapping the rest of the JSON is an exercise for you. Please refer to the docs. Also see this answer for more ideas: Karate - Match two dynamic responses

How to verify stringified json in pact

I am trying to build a pact between two services using asynchronous communication.
This is the code I used for generate the pact:
#ExtendWith(PactConsumerTestExt.class)
#PactTestFor(providerName = "provider", providerType = ProviderType.ASYNCH)
public class StringifiedPactTest {
#Pact(consumer = "consumer", provider = "provider")
public MessagePact generatePact(MessagePactBuilder builder) {
return builder.hasPactWith("provider")
.expectsToReceive("A valid aws sns event")
.withContent(new PactDslJsonBody().stringType(new String[]{"MessageId", "TopicArn"}).stringValue("Message", new PactDslJsonBody().stringType("Value", "Foo").toString()))
.toPact();
}
#Test
#PactTestFor(pactMethod = "generatePact")
public void buildPact(List<Message> messages) {
}
}
And the generated pact is
{
"consumer": {
"name": "consumer"
},
"provider": {
"name": "provider"
},
"messages": [
{
"description": "A valid aws sns event",
"metaData": {
"contentType": "application/json"
},
"contents": {
"TopicArn": "string",
"Message": "{\"Value\":\"Foo\"}",
"MessageId": "string"
},
"matchingRules": {
"body": {
"$.MessageId": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.TopicArn": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "4.0.10"
}
}
}
This means the producer should have a "Message" that matches {"Value" : "Foo"}, any other combination like {"Value" : "Bar" } won't be successful.
Is there any way to add matching rules inside a stringified json?
Thanks!
Here's an anonymised example from a test we have. Hope it's useful. This creates a pact that matches only on type. So on the provider side, when I test against the contract, it doesn't matter what value I have for categoryName for example, as long as it's a stringType:
#PactTestFor(providerName = "provider-service", providerType = ProviderType.ASYNCH)
public class providerServiceConsumerPactTest {
private static String messageFromJson;
#BeforeAll
static void beforeAll() throws Exception {
messageFromJson = StreamUtils.copyToString(new ClassPathResource("/json/pact/consumer-service_provider-service.json").getInputStream(), Charset.defaultCharset());
}
#Pact(provider = "provider-service", consumer = "consumer-service")
public MessagePact providerServiceMessage(MessagePactBuilder builder) {
DslPart body = new PactDslJsonBody()
.object("metaData")
.stringType("origin", "provider-service")
.datetimeExpression("dateCreated", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.closeObject()
.minArrayLike("categories", 0, 1)
.stringType("id", "example data")
.stringType("categoryName", "example data")
.booleanType("clearance", false)
.closeObject()
.closeArray();
return builder
.expectsToReceive("a provider-service update")
.withContent(body)
.toPact();
}
#Test
#PactTestFor(pactMethod = "providerServiceMessage")
public void testProviderServiceMessage(MessagePact pact) {
// State
final String messageFromPact = pact.getMessages().get(0).contentsAsString();
// Assert
JSONAssert.assertEquals(messageFromPact, messageFromJson, false);
}
I'm having exactly the same issue, and unfortunately I don't think it's possible to tell Pact to parse the stringified JSON and look inside it (e.g. to verify that parse(Message).Value === "Foo" in your example).
The best you can do is write a regular expression to match the string you're expecting. This kind of sucks because there's no easy way to ignore the ordering of the JSON keys (e.g. "{\"a\":\"1\", \"b\":\"2\"}" and "{\"b\":\"2\", \"a\":\"1\"}" will compare different) but AFAIK Pact simply lacks the parsing functionality we're looking for, so the only tool it provides is regex.

XML to JSon angular 4

I used an api that gives result in XML (follwing format) instead of JSON.
<soapenv:Envelope >
<soapenv:Header/>
<soapenv:Body>
<p444:DetailsResult>
<Policy>
<RenewalEligibility>YES</RenewalEligibility>
<ContCovBenEligibility>NO</ContCovBenEligibility>
<Client>
<FirstName>Pradeep</FirstName>
<LastName>Chaudhary</LastName>
<GenderCode>1</GenderCode>
<SmokerFlag>0</SmokerFlag>
<MaritalStatusCode>0</MaritalStatusCode>
</Client>
</Policy>
<WebAdress xsi:nil="true"/>
</p444:PolicyDetailsResult>
</soapenv:Body>
</soapenv:Envelope>
And i convert the above xml into JSON with xmlToJson method like this.
let jsonText = JSON.stringify(this.xmlToJson(xml));
let jsoParseData = JSON.parse(jsonText);
And I got result in below format.
{
"soapenv:Envelope": {
"soapenv:Body": {
"p444:DetailsResult": {
"Policy": {
"RenewalEligibility": "YES",
"ContCovBenEligibility": "NO",
"Client": {
"FirstName": "Pradeep",
"LastName": "Chaudhary",
"GenderCode": "1",
"SmokerFlag": "0",
"MaritalStatusCode": "0"
}
}
}
}
}
}
But I want to extract to JSON in below format
"Header": null,
"Body": {
"PolicyDetailsResult": {
"Policy": {
"RenewalEligibility": "YES",
"ContCovBenEligibility": "NO",
"Client":{
"FirstName":"Pradeep",
"LastName":"Chaudhary",
"GenderCode":"1",
"SmokerFlag":"0",
"MaritalStatusCode":"0",
}
}
}
}
Thanks in Advance

Iterate through two loops when building the JSON String

I am invoking a restful service to get the available documents on the the server where I am getting the JSON as s response. I am building the JSON String with the JSONBuilder so when invoking the this link
http://localhost:8080/httpConnector/Rest/Documents?Accept=application/json
I am getting the JSON String below:
{
"results": [
{
"result": {
"name": "Test traisy",
"version": "sdvdsv",
"author": "sdvdsv"
}
},
{
"result": {
"name": "Jaspersoft Ultimate guide",
"version": "sdfdsv",
"author": "sdvdsv"
}
},
{
"result": {
"name": "Dohrn",
"version": "12.19.00",
"author": "sdfdsf"
}
}
]
}
Code
String accept = getValue("Accept");
accept = "application/xml";
if ("application/xml".equals(accept)){
builder=new groovy.xml.MarkupBuilder(writer);
}else{
builder=new groovy.json.JsonBuilder();
}
builder{
results foaList.collect{
[
//Here I want to loop through the otaList to do something like that "ota.getName(), foa.getFlexiObject().getByString(ota.getName())"
result: [
name: it.getFlexiObject().getByString("name"),
version: it.getFlexiObject().getByString("version"),
author: it.getFlexiObject().getByString("author")
]
]
}
}
Now I want to add the properties programatically. Therefore I have to loop through the otaList to do something like that
builder.'results'() {
for(FlexiObjectAttachment foa: foaList){
for(ObjectTypeAttribute ota : otaList){
param.put(ota.getName(), foa.getFlexiObject().getByString(ota.getName()));
}
result(param);
}
}
this version just works for the xml respose.
What you can try is to do the combination of foa and ota directly in your collect call.
That way your initially created dict would have the correct structure.
Something like the example below
def foaList = [1, 2, 3, 4]
def otaList = ['A', 'B', 'C']
foaList.collect { foa ->
result = [name: "Name$foa", version: "v$foa", author: "Author$foa"]
otaList.each { ota -> result[ota] = "$ota$foa" }
[ result: result ]
}

Json.Net: Trimming an object to save web traffic

I have a very complex JSON returning from an API. I need to pass only the "first level" to the client side, without all the nested objects contained in it.
For example:
{
"name": "David",
"age": 5,
"school": {
"name": "Highschool",
"location": "AZ"
}
}
I'd like to pass to the client side only name & age, not "school".
Is there a simple way to do that?
You could parse the JSON into a JObject then copy all the "simple" properties (i.e. those that are not objects and arrays) to a new JObject. Then get the new JSON from the copy.
For example:
string json = #"
{
""name"": ""David"",
""age"": 5,
""school"": {
""name"": ""Highschool"",
""location"": ""AZ""
}
}";
JObject origObj = JObject.Parse(json);
JObject copyObj = new JObject();
foreach (JProperty prop in origObj.Properties())
{
if (prop.Value.Type != JTokenType.Object &&
prop.Value.Type != JTokenType.Array)
{
copyObj.Add(prop.Name, prop.Value);
}
}
json = copyObj.ToString();
Console.WriteLine(json);
The above will output the following:
{
"name": "David",
"age": 5
}