How to modify XML request payload using Javascript?

pxzxz1
New Member

Hello Apigeeks,

1. How do I update value in XML that comes from request payload?

2. How do I insert new element to the XML that comes from request payload?

For both items mentioned above, I tried with Javascript object model

var name = context.targetRequest.body.asXML.name;
request.content.asXML
context.proxyRequest.content.asXML

Unfortunately, these does not help me to do what I want. Basically,

1. I want to modify a value in XML, say <amount>0</amount> update to <amount>200</amount>

2. I want to add new element to the XML, say <student><name>Thomson</name></student> to <student><id>xx123</id><name>Thomson</name></student>

Not sure if it's matter, but it is a SOAP XML.

Solved Solved
0 12 4,113
1 ACCEPTED SOLUTION

Patricia,

It does not work for update (that does not involves adding new namespace)

What do you mean? It works for me, with the example payload you provided.

The JS error happens because your JavaScript tool does not recognize the e4x extensions. There's no error in the JS that I gave you.

Attached please find a working example.

apiproxy-e4x-update.zip

View solution in original post

12 REPLIES 12

couple options come to mind.

1) use a 3rd-party Javascript XML Library. Import it as a resource and use it in your proxy (JS policy).

2) use a java callout and use inbuilt XML capabilities or your preferred 3rd-party XML library.

Do you have a sample of it?

When you use the .asXML thing, you get an XML object. The behavior of that XML object is defined as a part of larger set of xml capabilities, under the umbrella of E4X, which stands for EcmaScript for XML. E4X was a standard extension to EcmaScript (aka JavaScript) for a while, but then later was deprecated. It is still supported in the Rhino JS engine that Apigee uses, still supported in Apigee.

There is a way to do what you want using E4X, within a JS callout in Apigee. Unfortunately because E4X was deprecated from mainline Javascript, the documentation for the capability can be difficult to find. Sometimes it can be difficult to figure out how you should do a particular thing. But I can show you.

This worked for me.

Starting with this SOAP message:

<soap:Envelope xmlns:ns1="urn://24F9C86E-D207-4049-850B-02C48F138AA1"
               xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
    <ns1:Anything/>
  </soap:Header>
  <soap:Body>
    <ns1:someMethod>
      <ns1:Status>
        <ns1:amount>0</ns1:amount>
        <ns1:student>
          <ns1:name>Thomson</ns1:name>
        </ns1:student>
      </ns1:Status>
    </ns1:someMethod>
  </soap:Body>
</soap:Envelope>
 

I can use this JS code:

var message = context.getVariable('name_of_source_message');
if (message && message.content) {
  var xml = new XML(message.content);
  var ns1 = new Namespace('ns1','urn://24F9C86E-D207-4049-850B-02C48F138AA1');
  var soap = new Namespace('soap','http://www.w3.org/2003/05/soap-envelope');
  var body = xml.soap::Body;
  // modify the text value of a specific node
  var amountTextNode = body.ns1::someMethod.ns1::Status.ns1::amount.text();
  amountTextNode.parent().setChildren('200');
  // insert a new element
  var studentNode = body.ns1::someMethod.ns1::Status.ns1::student;
  var fragment = new XML('<id>xx123</id>');
  fragment.setNamespace(ns1);
  studentNode.appendChild(fragment);
  // replace the content of the message with the modified version
  message.content = xml.toXMLString();
}

to get this result:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" 
               xmlns:ns1="urn://24F9C86E-D207-4049-850B-02C48F138AA1">
  <soap:Header>
    <ns1:Anything/>
  </soap:Header>
  <soap:Body>
    <ns1:someMethod>
      <ns1:Status>
        <ns1:amount>200</ns1:amount>
        <ns1:student>
          <ns1:name>Thomson</ns1:name>
          <ns1:id>xx123</ns1:id>
        </ns1:student>
      </ns1:Status>
    </ns1:someMethod>
  </soap:Body>
</soap:Envelope>

You said:

Not sure if it's matter, but it is a SOAP XML.

Yes, it does matter. If you have a SOAP source message, you will need to deal with namespaces. I hope the code above shows you how to do that.

Hello @Dino-at-Google

Thanks for your help. Unfortunately, the payload does not get updated.

This is the request payload that I will be receiving.

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:xsd="http://www.w3.org/2001/XMLSchema"
		xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
		xmlns:urn="urn:24F9C86E-D207-4049-850B-02C48F138AA1"
		xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
   <soapenv:Header/>
   <soapenv:Body>
      <urn:SomeMethod soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <Amount xsi:type="xsd:string">0</Amount>
         <Student xsi:type="urn:3PBP0EU83" xmlns:urn="urn:UT21X4EM9">
            <Name xsi:type="xsd:string">Thomson</Name>
         </Student>
      </urn:SomeMethod>
   </soapenv:Body>
</soapenv:Envelope>

This is the new element that I want to insert.

<ID xsi:type="xsd:string"></ID>

My Javascript as I followed your step.

// get student id
var studentid = "xx123";

// read soap xml from request payload
var message = context.getVariable("request.content");

// modify request payload before sending to backend server
if (message && message.content) {
   var xml = new XML(message.content);
   var soapenv = new Namespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
   var urn = new Namespace("urn", "urn:24F9C86E-D207-4049-850B-02C48F138AA1");
   var someMethod = xml.soapenv::Body.urn::SomeMethod;

   // modify the text value of a specific node
   var amountTextNode = someMethod.Amount.text();
   amountTextNode.parent().setChildren("200");

   // insert new element
   var studentNode = someMethod.Student;
   var fragXML = "<ID>" + studentid + "<ID>";
   var fragment = new XML(fragXML);
   studentNode.appendChild(fragment);

   // replace the content of the message with the modified version
   message.content = xml.toXMLString();
}

There is no error, but the payload does not get updated.

yes, you need to modify the code that I showed you.

The code I wrote works with MY XML. It won't work with YOUR XML. Your XML has different elements, different namespaces. For example, your Student node has no XML namespace. (You noticed that). As another example, adding a namespace-qualified attribute, like xsi:type="xsd:string", you need to use some specific syntax.

This worked for me.

var message = context.getVariable(properties.sourceMessage);
var newStudentId = 'xx123';
if (message && message.content) {
  var xml = new XML(message.content);

  // get references to the namespaces uses in this document
  var ns1 = new Namespace('ns1','urn:24F9C86E-D207-4049-850B-02C48F138AA1');
  var soap = new Namespace('soap','http://schemas.xmlsoap.org/soap/envelope/');
  var someMethod = xml.soap::Body.ns1::SomeMethod;

  // modify the text value of a specific node
  var amountTextNode = someMethod.Amount.text();
  amountTextNode.parent().setChildren('200');

  // insert a new node under Student
  var studentNode = someMethod.Student;
  var fragment = new XML('<ID>' + newStudentId + '</ID>');
  var xsi = new Namespace('xsi','http://www.w3.org/2001/XMLSchema-instance');
  fragment.addNamespace(xsi);
  fragment.@xsi::type = 'xsd:string';
  studentNode.appendChild(fragment);

  // replace the content of the message with the modified version
  message.content = xml.toXMLString();
}

The output I saw:

  <soapenv:Body>
    <urn:SomeMethod soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <Amount xsi:type="xsd:string">200</Amount>
      <Student xmlns:urn="urn:UT21X4EM9" xsi:type="urn:3PBP0EU83">
        <Name xsi:type="xsd:string">Thomson</Name>
        <ID xsi:type="xsd:string">xx123</ID>
      </Student>
    </urn:SomeMethod>
  </soapenv:Body>

Let me know if this helps.

Hello @Dino-at-Google

It does not work for update (that does not involves adding new namespace) and adding new element. I think it's because of the js error.

10871-js-error.jpg

  1. Where do you place your js policy?
  2. Do you mind to share your proxy?

Patricia,

It does not work for update (that does not involves adding new namespace)

What do you mean? It works for me, with the example payload you provided.

The JS error happens because your JavaScript tool does not recognize the e4x extensions. There's no error in the JS that I gave you.

Attached please find a working example.

apiproxy-e4x-update.zip

@dino-at-Google

Thanks for sharing. Your js works on my side as well.

The issue occurs when I modified to POST payload. I'm guessing it is on the verb and/or context.getvariable which I am using request.content.

Can you advise if I am missing any properties setup? I attached the proxy.

test-soap-xml-rev2-2021-03-16.zip

You have modified the JS to have this as the first statement:

var message = context.getVariable('request.payload');

The first statement should be this:

var message = context.getVariable('request');

When I made this change, your proxy worked for me.

curl $endpoint/test-soap-xml-e4x/updatepayload \
  -X POST  \
  -H content-type:application/xml \
   -d @sample-request-payload.xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:24F9C86E-D207-4049-850B-02C48F138AA1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Header/> <soapenv:Body> <urn:SomeMethod soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <Amount xsi:type="xsd:string">200</Amount> <Student xmlns:urn="urn:UT21X4EM9" xsi:type="urn:3PBP0EU83"> <Name xsi:type="xsd:string">Thomson</Name> <ID xsi:type="xsd:string">xx123</ID> </Student> </urn:SomeMethod> </soapenv:Body> </soapenv:Envelope>

The sample-request-payload.xml looks like this:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                xmlns:urn="urn:24F9C86E-D207-4049-850B-02C48F138AA1"
                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
   <soapenv:Header/>
   <soapenv:Body>
      <urn:SomeMethod soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <Amount xsi:type="xsd:string">0</Amount>
         <Student xsi:type="urn:3PBP0EU83" xmlns:urn="urn:UT21X4EM9">
            <Name xsi:type="xsd:string">Thomson</Name>
         </Student>
      </urn:SomeMethod>
   </soapenv:Body>
</soapenv:Envelope>


Attached please find the modified proxy.

apiproxy-test-soap-xml-e4x-20210316-152042.zip

this dosen't work for me neither

Hi,

I want to replace SubcriberID="1234" to SubscriberID="5678". Please advise.

Below is my sample request xml.

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://xml.company.com/transaction">
<SOAP-ENV:Header>
<tns:Subscriber SubscriberID="1234" UserName="xxx"/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Please do not ask new questions in responses to old threads. It breaks search and usability for everyone.