{ Community }
  • Academy
  • Docs
  • Developers
  • Resources
    • Community Articles
    • Apigee on GitHub
    • Code Samples
    • Videos & eBooks
    • Accelerator Methodology
  • Support
  • Ask a Question
  • Spaces
    • Product Announcements
    • General
    • Edge/API Management
    • Developer Portal (Drupal-based)
    • Developer Portal (Integrated)
    • API Design
    • APIM on Istio
    • Extensions
    • Business of APIs
    • Academy/Certification
    • Adapter for Envoy
    • Analytics
    • Events
    • Hybrid
    • Integration (AWS, PCF, Etc.)
    • Microgateway
    • Monetization
    • Private Cloud Deployment
    • 日本語コミュニティ
    • Insights
    • IoT Apigee Link
    • BaaS/Usergrid
    • BaaS Transition/Migration
    • Apigee-127
    • New Customers
    • Topics
    • Questions
    • Articles
    • Ideas
    • Leaderboard
    • Badges
  • Log in
  • Sign up

Get answers, ideas, and support from the Apigee Community

  • Home /
  • Edge/API Management /
avatar image
1
Question by Pawan Bisht · Feb 18 at 12:29 PM · 48 Views javascriptxmlxmltojson

Can we convert object into XML inside Javascipt

I"m working in a scenario where I'm getting xml object from the httpClient's getResponse().content. And I need to deal with values that are present in the response. But its not allowing me to extract those values in javascript.

Note: I cannot use XML-to-JSON like policies in my use-case; I can only work with javascript. Because I'm calling multiple HTTP requests based on the ids present in the input.

var url = properties.endpoint + '?billingId=' + billingIds[index];
var myRequest = new Request(url, "GET");

context.setVariable("myRequesttest", myRequest);
var exchange = httpClient.send(myRequest);
exchange.waitForComplete();

if (exchange.isSuccess()) {
    var responseObj = exchange.getResponse().content;
    context.setVariable("response.content", responseObj);
    context.setVariable("myResponse", responseObj);
} else if (exchange.isError()) {
  context.setVariable("myResponseError", exchange.getError());
}

help?

Comment
Add comment
10 |5000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by Apigeeks only
  • Viewable by the original poster
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Close

1 Answer

  • Sort: 
avatar image
0
Best Answer

Answer by Dino-at-Google   · Feb 19 at 12:51 AM

You can use the JS callout to invoke multiple external systems (via the builtin httpClient), and you can then use logic in the JS to combine the results into one object or payload. This is nice if the number of external systems you need to invoke is not determined until runtime. Maybe it's dependent upon the data provided in the input request, or based on data returned by the first call. So this is handy.

Having said that, there are limitations. The JS callout has a timeout on it, and if execution time exceeds that timeout, then the JS policy will fail. If the responses from your remote systems do not all return before the JS policy timeout expires, you won't get the results. Also, in a JS callout you cannot use the various npm modules that help you out with parsing XML and so on. For a more general solution then, I'd suggest a dedicated app logic host - something like Google App Engine, Cloud Run, or similar. But you can do it with a JS callout if you are comfortable with the limitations.

OK with the caveats and warnings out of the way, this is what worked for me.

// invokeTransformAndCombine.js

var baseUrl = 'https://my-base-url.com/foobar';
var ids = [182872, 19882, 2133];
var accumulator = {};

// the asynchronous response handler
function onComplete(id) {
  return function(response, error) {
    if (response) {
      if (response.status == 200) {
        var xmlobj = new XML(response.content);
        var json = E4XtoJSON(xmlobj);
        accumulator[id] = json;
        if (Object.keys(accumulator).length == ids.length) {
          context.setVariable('combined', JSON.stringify(accumulator, null, 2));
        }
      }
    }
    else {
      context.setVariable('requesterror_' + id, 'Whoops: ' + error);
    }
  };
}

// send out one request
function invokeOne(id) {
  var url = baseUrl + '?id=' + id ;
  var headers = {
        Authorization : 'optional, pass whatever you like',
        'ID-Index' : id
      };
  var req = new Request(url, 'GET', headers);
  httpClient.send(req, onComplete(id));
}

// set the default value (empty object)
context.setVariable('combined', JSON.stringify(accumulator, null, 2));

// action starts here
ids.forEach(invokeOne);

Let me explain the code.

  1. First, the real action starts at the line with ids.forEach(). This forEach invokes a function one time for each of the ID's in the array.
  2. The invokeOne function uses the httpClient.send() method. This form is a little different than what you were using. It employs the callback function as the 2nd argument. The way this one works: httpClient.send() sends out the request, and ... returns. When the http runtime receives the response to that request, it invokes the callback function to handle the response.
  3. onComplete() is not a callback function. It is a function that returns a callback function, wrapped in a closure. The reason we want this: that way the callback has access to the ID that the response corresponds to. The inner function (within the closure) is the actual callback function.
  4. The inner callback - there is some magic going on there. It checks the status code and only in the case of success (200 code) it does a few things.

        var xmlobj = new XML(response.content);
        var json = E4XtoJSON(xmlobj);
        accumulator[id] = json;
        if (Object.keys(accumulator).length == ids.length) {
          context.setVariable('combined', JSON.stringify(accumulator, null, 2));
        }
    	
    1. First, the JS callout in Apigee supports E4X, which is the moniker for Ecmascript-for-XML. That extension to the Ecmascript language has since been deprecated in the main line of Ecmascript as driven by the TC39 Tech committee. But it still works in Apigee. So this line of code converts the text XML response into an XML 'object', which can then be handled in JS.
    2. Then, it invokes the mystical function E4XtoJSON. This is defined elsewhere, but as you can infer from the name, this function converts the XML object to JSON.
    3. it drops that json as a value in the accumulator object.
    4. and when enough responses have been received, it stringifies that accumulator into a context variable.

All of that should be pretty clear. The remaining mystery is the E4XtoJSON function. I got that from this gist. To use it within a JS inside Apigee, I had to drop that code into a separate 'resource' under the jsc directory in the proxy bundle, and then configure the policy this way:

<Javascript name='JS-InvokeTransformAndCombine' timeLimit='2200' >
  <IncludeURL>jsc://E4XtoJSON.js</IncludeURL>
  <ResourceURL>jsc://invokeTransformAndCombine.js</ResourceURL>
</Javascript>

When I invoke this repeatedly with a service that returns XML shaped like this:

<billinfo>
  <stamp>1613696215531</stamp>
  <localtime>2021-02-19T00:56:55Z</localtime>
  <id>902</id>
  <status>rejected</status>
  <code>JN23H</code>
  <amount>376.44</amount>
</billinfo>

... the output of the Javascript I showed above is a combined JSON blob that looks like this:

{
  "19882": {
    "stamp": "1613696265138",
    "localtime": "2021-02-19T00:57:45Z",
    "id": "19882",
    "status": "ready",
    "code": "E8237k",
    "amount": "788.10"
  },
  "182872": {
    "stamp": "1613696265136",
    "localtime": "2021-02-19T00:57:45Z",
    "id": "182872",
    "status": "split",
    "code": "JN23H",
    "amount": "280.94"
  },
  "2133": {
    "stamp": "1613696265141",
    "localtime": "2021-02-19T00:57:45Z",
    "id": "2133",
    "status": "rejected",
    "code": "SIWU97",
    "amount": "847.50"
  }
}

If you don't want the full combined thing, but rather just a few fields extracted from the XML response, you can take the same approach but just grab the fields you want.

I haven't tried the E4XtoJSON with XML that uses namespaces. That's a wrinkle you will have to sort out.

helpful?

Comment
Add comment Show 6 · Link
10 |5000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by Apigeeks only
  • Viewable by the original poster
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Pawan Bisht · Feb 19 at 05:51 AM 0
Link

Hi Dino, thank you so much for your response, but I'm facing a problem while using this javascript.
I'm getting this error:

Execution of JS-PrepareCheckOutagesResponse failed with error: Javascript runtime error: "ReferenceError: "require" is not defined. (E4XtoJSON.js:54)"
avatar image Dino-at-Google ♦♦ Pawan Bisht   · Feb 19 at 06:04 AM 0
Link

You need to remove line 54 and below from the gist. Those lines are only for testing.

avatar image Pawan Bisht Dino-at-Google ♦♦ · Feb 19 at 06:10 AM 0
Link

Got it, thanks again.

avatar image Pawan Bisht Dino-at-Google ♦♦ · Feb 19 at 08:02 AM 0
Link

Hi Dino, in my javascript asynchronous code is not working. I tried to print data inside the callback function but didn't get any data in the console and after callback function, every statement is getting executed.

Show more comments

Follow this Question

Answers Answers and Comments

84 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

XML to JSON policy single array element 3 Answers

Unable to convert JsonToXml and XmlToJson data. 2 Answers

JSON to XML: Invalid ObjectRootElementName with Colon 1 Answer

How to Traverse XML hierarchy using .asXML 1 Answer

How the get only one specific response in apigee? 1 Answer

  • Products
    • Edge - APIs
    • Insights - Big Data
    • Plans
  • Developers
    • Overview
    • Documentation
  • Resources
    • Overview
    • Blog
    • Apigee Institute
    • Academy
    • Documentation
  • Company
    • Overview
    • Press
    • Customers
    • Partners
    • Team
    • Events
    • Careers
    • Contact Us
  • Support
    • Support Overview
    • Documentation
    • Status
    • Edge Support Portal
    • Privacy Policy
    • Terms & Conditions
© 2021 Apigee Corp. All rights reserved. - Apigee Community Terms of Use - Powered by AnswerHub
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Create an article
  • Post an idea
  • Spaces
  • Product Announcements
  • General
  • Edge/API Management
  • Developer Portal (Drupal-based)
  • Developer Portal (Integrated)
  • API Design
  • APIM on Istio
  • Extensions
  • Business of APIs
  • Academy/Certification
  • Adapter for Envoy
  • Analytics
  • Events
  • Hybrid
  • Integration (AWS, PCF, Etc.)
  • Microgateway
  • Monetization
  • Private Cloud Deployment
  • 日本語コミュニティ
  • Insights
  • IoT Apigee Link
  • BaaS/Usergrid
  • BaaS Transition/Migration
  • Apigee-127
  • New Customers
  • Explore
  • Topics
  • Questions
  • Articles
  • Ideas
  • Badges