Is it possible to replace an XML node in a document, with a single policy?

Is there a way to add an xml node to a document, in Apigee Edge? Or to replace an XML node?

I have a soap request which needs to have the text of an element replaced with different text, before sending to backend target. Ideally I'd point to the node to be replaced with an XPath expression. The node pointed to by the XPath expression is then completely replaced by the new node.

To illustrate further, here is the doc for a feature in the Oracle Gateway, which does this.

Can Apigee Edge do that? Any sample JS reference would be great.

-Vinay

Solved Solved
1 9 2,269
1 ACCEPTED SOLUTION

Hi Vijay,

as I said, there's no out-of-the-box policy that does what you want. Of course you could do it with an XSLT policy, but not many people like to write and maintain XSLT code.

But it's pretty easy to create custom policies in Apigee Edge. So I wrote one that does this.

It's available on github.

I published the Java source code, but you actually don't need to look at the Java or even compile it. You just need the jar file. What you'll do is configure a Java callout that references the jar, and sets the appropriate properties - things like the xpath of the node you want to replace, and the xml namespaces required by the xpath, and so on.

I have also published an example apiproxy bundle that does just what you want. It's on the same github repo. It shows exactly how to configure it to do what you described.

For example, here's the configuration for the policy for the scenario above:

<JavaCallout name='Java-AddXmlNode-2'>
  <Properties>
    <Property name='xmlns:soap'>http://schemas.xmlsoap.org/soap/envelope/</Property>
    <Property name='xmlns:act'>http://yyyy.com</Property>
    <Property name='source'>request.content</Property>
    <Property name='new-node-type'>text</Property>
    <Property name='new-node-text'>{request.queryparam.texttoinsert}</Property>
    <Property name='xpath'>/soap:Envelope/soap:Body/act:test/abc/act:demo/text()</Property>
    <Property name='action'>replace</Property>
  </Properties>
  <ClassName>com.dinochiesa.edgecallouts.AddXmlNode</ClassName>
  <ResourceURL>java://edge-custom-add-xml-node.jar</ResourceURL>
</JavaCallout>

All of that should mostly make sense.

And you can even try it out. I have the bundle deployed and running, so you can use this to test it:

curl -i -X POST -H content-type:text/xml \
  'http://deecee-test.apigee.net/add-xml-node/t2?texttoinsert=decrypted-text-here' \
 -d  '
 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
  <soapenv:Header/> 
  <soapenv:Body> 
    <act:test xmlns:act="http://yyyy.com"> 
      <abc> 
        <act:demo>fokyCS2jrkE5s+bC25L1Aax5sK//FkYA1msxIyW7prOun0VwoSET73UXKyKJ7nmd3OwHq/08GXIpwlq3QBJuG7a4Xgm4Vk</act:demo> 
      </abc> 
    </act:test> 
  </soapenv:Body> 
</soapenv:Envelope> 
'

The result should show you the text of the <act:demo> element replaced with the text supplied in the query param. Like so:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
  <soapenv:Header/> 
  <soapenv:Body> 
    <act:test xmlns:act="http://yyyy.com"> 
      <abc> 
        <act:demo>decrypted-text-here</act:demo> 
      </abc> 
    </act:test> 
  </soapenv:Body> 
</soapenv:Envelope>

This should get you what you want.

Let me know if you have questions or problems.

View solution in original post

9 REPLIES 9

Vinay, There's no out-of-the-box policy that does this. But I might be able to help you.

what kind of Node? Text, Element, Attribute? And what is the source of the value of the node? Is it a hard-coded value? Can you give me some more detail?

It's the element which needs to be replaced.

The value is from message context variable which is resulted from the java call out.

Receive SOAP request with the encrypted demo element

In Apigee we extract the demo element. Use Java code to decrypt demo value.

###This is where we need to replace the XML element### Construct the payload with the decrypted demo value Post the message to target.

Eg:

<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> </soap:Header> <soapenv:Body> <act:test xmlns:act="http://yyyy.com"> <abc> <act:demo>fokyCS2jrkE5s+bC25L1Aax5sK//FkYA1msxIyW7prOun0VwoSET73UXKyKJ7nmd3OwHq/08GXIpwlq3QBJuG7a4Xgm4Vk</act:demo> </abc> </act:test> </soapenv:Body> </soapenv:Envelope> <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> </soap:Header> <soapenv:Body> <act:test xmlns:act="http://yyyy.com"> <abc> <act:demo>{decrytedValue}</act:demo> </abc> </act:test> </soapenv:Body> </soapenv:Envelope>

Hi Vijay,

as I said, there's no out-of-the-box policy that does what you want. Of course you could do it with an XSLT policy, but not many people like to write and maintain XSLT code.

But it's pretty easy to create custom policies in Apigee Edge. So I wrote one that does this.

It's available on github.

I published the Java source code, but you actually don't need to look at the Java or even compile it. You just need the jar file. What you'll do is configure a Java callout that references the jar, and sets the appropriate properties - things like the xpath of the node you want to replace, and the xml namespaces required by the xpath, and so on.

I have also published an example apiproxy bundle that does just what you want. It's on the same github repo. It shows exactly how to configure it to do what you described.

For example, here's the configuration for the policy for the scenario above:

<JavaCallout name='Java-AddXmlNode-2'>
  <Properties>
    <Property name='xmlns:soap'>http://schemas.xmlsoap.org/soap/envelope/</Property>
    <Property name='xmlns:act'>http://yyyy.com</Property>
    <Property name='source'>request.content</Property>
    <Property name='new-node-type'>text</Property>
    <Property name='new-node-text'>{request.queryparam.texttoinsert}</Property>
    <Property name='xpath'>/soap:Envelope/soap:Body/act:test/abc/act:demo/text()</Property>
    <Property name='action'>replace</Property>
  </Properties>
  <ClassName>com.dinochiesa.edgecallouts.AddXmlNode</ClassName>
  <ResourceURL>java://edge-custom-add-xml-node.jar</ResourceURL>
</JavaCallout>

All of that should mostly make sense.

And you can even try it out. I have the bundle deployed and running, so you can use this to test it:

curl -i -X POST -H content-type:text/xml \
  'http://deecee-test.apigee.net/add-xml-node/t2?texttoinsert=decrypted-text-here' \
 -d  '
 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
  <soapenv:Header/> 
  <soapenv:Body> 
    <act:test xmlns:act="http://yyyy.com"> 
      <abc> 
        <act:demo>fokyCS2jrkE5s+bC25L1Aax5sK//FkYA1msxIyW7prOun0VwoSET73UXKyKJ7nmd3OwHq/08GXIpwlq3QBJuG7a4Xgm4Vk</act:demo> 
      </abc> 
    </act:test> 
  </soapenv:Body> 
</soapenv:Envelope> 
'

The result should show you the text of the <act:demo> element replaced with the text supplied in the query param. Like so:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
  <soapenv:Header/> 
  <soapenv:Body> 
    <act:test xmlns:act="http://yyyy.com"> 
      <abc> 
        <act:demo>decrypted-text-here</act:demo> 
      </abc> 
    </act:test> 
  </soapenv:Body> 
</soapenv:Envelope>

This should get you what you want.

Let me know if you have questions or problems.

That was quick.Thanks for your solution.

Hi Vinay, did it work for you?

Yes. Thankyou.

Hi Dino,

I am having an issue in the Java callout.

I want to edit the following values in the SOAP request:

<Username>{User}</Username>

<Password>{Pwd}</Password>

<v3:Context>aaa</v3:Context>

 <v3:userName>xxx</v3:userName>

 <v3:password>zzz</v3:password>

 <v3:applicationName>app_name</v3:applicationName>


My SOAP request

<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:v3="http://www.mynamespace">
    <soapenv:Header>
        <Security
            xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <UsernameToken>
                <Username>{User}</Username>
                <Password>{Pwd}</Password>
            </UsernameToken>
        </Security>
    </soapenv:Header>
    <soapenv:Body>
        <v3:AuthenticateUserRequest>
            <v3:Context>aaa</v3:Context>
            <v3:userName>xxx</v3:userName>
            <v3:password>zzz</v3:password>
            <v3:applicationName>app_name</v3:applicationName>
        </v3:AuthenticateUserRequest>
    </soapenv:Body>
</soapenv:Envelope>

Java callout policy

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JavaCallout async="false" continueOnError="false" enabled="true" name="ChangeNodeValue">
    <DisplayName>ChangeNodeValue</DisplayName>
    <Properties>
        <Property name="xmlns:soap">http://schemas.xmlsoap.org/soap/envelope/</Property>
        <Property name="xmlns:v3">http://yyyy.com</Property>
        <Property name="source">{sim_payload}</Property>
        <Property name="new-node-type">text</Property>
        <Property name="new-node-text">{userName}</Property>
        <Property name="xpath"></Property>
        <Property name="action">replace</Property>
    </Properties>
    <ClassName>com.dinochiesa.edgecallouts.EditXmlNode</ClassName>
    <ResourceURL>java://edge-custom-edit-xml-node.jar</ResourceURL>
</JavaCallout>

I am getting editxml_exception and editxml_error. Please let me know where I am going wrong.

To start with , I began editing only the Username node.

Thanks

The xpath property was missed out .

<Property name = "xpath">/soapenv:Envelope/soapenv:Header/child::*[local-name() = 'Security' and namespace-uri() = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd']/child::*[local-name() = 'UsernameToken' and namespace-uri() = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd']</Property>

Hi -

please ask new questions with the "ask a question" button! and I will answer there.

3834-ask-a-question-2.png