Replacing xml node values with custom values

Hi,

I want to replace existing xml values with custom values. The xml is a soap request payload.

<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">
            <Token>
                <Username>{User}</Username>
                <Password>{Pwd}</Password>
            </Token>
        </Security>
    </soapenv:Header>
    <soapenv:Body>
        <v3:AuthenticateUserRequest>
            <v3:Context>aaa</v3:Context>
            <v3:userName>xxx</v3:userName>
            <v3:password>zzz</v3:password>
            <v3:application>app_name</v3:application>
        </v3:AuthenticateUserRequest>
    </soapenv:Body>
</soapenv:Envelope>

I want to replace the userName , Password , Context and application fields at runtime.

I want to use javascript or python callouts.

Thanks

Solved Solved
1 12 6,041
1 ACCEPTED SOLUTION

Hi - can you elaborate a little more on your scenario?

When you say "replace values in an XML document", should we think of the original document as a static template? It's not something that's being sent in with the request, am I right?

In that case you can embed the XML template into an AssignMessage policy, and just use regular message template rules, in which you place variable references inside curly braces. For example, something like this:

<AssignMessage name='AM-SampleSoap'>
  <Set>
    <Payload contentType='application/xml'>
<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">
            <Token>
                <Username>{User}</Username>
                <Password>{Pwd}</Password>
            </Token>
        </Security>
    </soapenv:Header>
    <soapenv:Body>
        <v3:AuthenticateUserRequest>
            <v3:Context>{aaa}</v3:Context>
            <v3:userName>{xxx}</v3:userName>
            <v3:password>{zzz}</v3:password>
            <v3:application>{app_name}</v3:application>
        </v3:AuthenticateUserRequest>
    </soapenv:Body>
</soapenv:Envelope>
</Payload>
  </Set>
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
  <AssignTo createNew='false' transport='http' type='request'/>
</AssignMessage>


In the above case you would need to have all of the strings within curly braces hold values as context variables in the flow within Edge.

Or, maybe your scenario is one in which an XML payload is being sent in with the request, and you want to modify that payload, transform it in some way. XML-to-XML. In that case I highly recommend using the XSLT policy within Apigee Edge for doing so. Once again you can employ context variables to insert values where appropriate, and you can remove values or elements (eg, remove the soap:Header). I won't show an example of the XSLT, because... well it's just XSLT and it wouldn't make sense unless we know exactly what the input document is, and exactly what the output document needs to be.

If you for whatever reason do not like the idea of XSLT, you could use the Edit-Xml-Node Java callout. But that works on exactly one node at a time, so if you wanted to replace 4 text values, and then delete the soap:header, that would be 5 different policy instances. It would work but it wouldn't be the most efficient way to do things, and therefore I would recommend that you avoid this option in this case. The Edit-Xml-Node callout is good for modifying ONE node in a document.

As for using JavaScript or Python - you certainly could do that. I can imagine embedding the XML string into a KVM entry, reading it into a context variable, then doing string manipulation within JavaScript in order to replace the variables that you want to replace. A more structured way of doing this would be to use handlebars from within a nodejs target. But that maybe overkill. Either of these approaches feels less clean to me, than the AssignMessage policy I described up top.

View solution in original post

12 REPLIES 12

Hi @Ramnath,

I hope this is what you would be looking for Replace XML Node in Apigee

@Aswin Segu

Please avoid link only answers, Use comments if you want to redirect.

Hi - can you elaborate a little more on your scenario?

When you say "replace values in an XML document", should we think of the original document as a static template? It's not something that's being sent in with the request, am I right?

In that case you can embed the XML template into an AssignMessage policy, and just use regular message template rules, in which you place variable references inside curly braces. For example, something like this:

<AssignMessage name='AM-SampleSoap'>
  <Set>
    <Payload contentType='application/xml'>
<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">
            <Token>
                <Username>{User}</Username>
                <Password>{Pwd}</Password>
            </Token>
        </Security>
    </soapenv:Header>
    <soapenv:Body>
        <v3:AuthenticateUserRequest>
            <v3:Context>{aaa}</v3:Context>
            <v3:userName>{xxx}</v3:userName>
            <v3:password>{zzz}</v3:password>
            <v3:application>{app_name}</v3:application>
        </v3:AuthenticateUserRequest>
    </soapenv:Body>
</soapenv:Envelope>
</Payload>
  </Set>
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
  <AssignTo createNew='false' transport='http' type='request'/>
</AssignMessage>


In the above case you would need to have all of the strings within curly braces hold values as context variables in the flow within Edge.

Or, maybe your scenario is one in which an XML payload is being sent in with the request, and you want to modify that payload, transform it in some way. XML-to-XML. In that case I highly recommend using the XSLT policy within Apigee Edge for doing so. Once again you can employ context variables to insert values where appropriate, and you can remove values or elements (eg, remove the soap:Header). I won't show an example of the XSLT, because... well it's just XSLT and it wouldn't make sense unless we know exactly what the input document is, and exactly what the output document needs to be.

If you for whatever reason do not like the idea of XSLT, you could use the Edit-Xml-Node Java callout. But that works on exactly one node at a time, so if you wanted to replace 4 text values, and then delete the soap:header, that would be 5 different policy instances. It would work but it wouldn't be the most efficient way to do things, and therefore I would recommend that you avoid this option in this case. The Edit-Xml-Node callout is good for modifying ONE node in a document.

As for using JavaScript or Python - you certainly could do that. I can imagine embedding the XML string into a KVM entry, reading it into a context variable, then doing string manipulation within JavaScript in order to replace the variables that you want to replace. A more structured way of doing this would be to use handlebars from within a nodejs target. But that maybe overkill. Either of these approaches feels less clean to me, than the AssignMessage policy I described up top.

Hi

The scenario is as below

I am inserting the xml string in a KVM and at runtime I want to modify the xml node values and send it to the target server. ( the last scenario that you have mentioned above )

Since java callout modifies only a single node , that wouldn't be a recommended approach. So I prefer either a javascript or a python callout. But there is some difficulty in editing xml node values with javascript callout. I am not able to modify the values.

For python callout, I am trying to use ElementTree module and also tied with soupparser module, but with no success

Python callout

import xml.etree.ElementTree as ET

xml = flow.getVariable("xml_payload")

root = ET.fromstring(xml)


res = root.findall("/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']")


print (res)

Error :

File "ParseSIMPayload.py", line 8, in <module>
  File "/apps/opt/software/apigee/edge-gateway-4.16.05-0.0.598/lib/thirdparty/jython-secure-2.5.2.jar/Lib/xml/etree/ElementTree.py", line 355, in findall

Any help on python or javascript callouts would be great

Thanks

I'm not a fan of using Python to write code that performs surgery on SOAP documents. For some reason I feel better about using XSLT for that. See my other comment for a working example.

I haven't tried xslt transformation . Will try that out too.

I tried with the xslt policyapproach.

1) Stored my xml payload in a kvm

2) Retrieved it using kvm operations policy

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations async="false" continueOnError="false" enabled="true" name="ExtractPayloadFromKVM" mapIdentifier="user-authentication-payloads">
    <DisplayName>ExtractPayloadFromKVM</DisplayName>
    <FaultRules/>
    <Properties/>
    <ExclusiveCache>false</ExclusiveCache>
    <ExpiryTimeInSecs>-1</ExpiryTimeInSecs>
    <Get assignTo="payload">
        <Key>
            <Parameter ref="request.header.env"/>
        </Key>
    </Get>
    <Scope>environment</Scope>
</KeyValueMapOperations>

3) Stored the xml payload in an assign message policy since xslt only takes message as source and not a string

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="SetXMLPayload">
    <DisplayName>SetXMLPayload</DisplayName>
    <Description>create a new message</Description>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <Set>
        <Payload contentType="application/xml" variablePrefix="%" variableSuffix="#">%payload#</Payload>
    </Set>
    <AssignTo createNew="true" type="request">newlyCreatedMessage</AssignTo>
</AssignMessage>

But , while tracing the call the variable newlyCreatedMessage is empty. I expect it to be populated with the xml payload that will be further used in the XSLT policy.

Please help on this.

Thanks

I put together an example for you. Please find here a ZIP file containing an API proxy, as well as a bash script to provision it into an Edge org + environment.

The bash script also invokes the API proxy if you are running in the public Edge cloud.

The proxy does these things:

  • uses KVM-Get to retrieve an XML file from KVM
  • uses AssignMessage to store that into a "message" variable
  • uses XSLT to transform that message
  • returns the result to the caller

The bash script provisions the proxy and the required KVM entry, and sends a request into the proxy. See the README in the zip for more information.

apigee-edge-kvm-xslt-example-20161029-1329.zip

Thanks Dino. The xslt transormation works !.

Just curious to know why does the 'newlyCreatedMessage' variable in the Assign Message policy doesn't get populated with the input xml payload from the KVM in the trace , but works fine as an input to the XSLT policy ?

Not able to see newlyCreatedMessage in trace:

<AssignTo createNew="true" type="request">newlyCreatedMessage</AssignTo>

Source for xslt (this works perfectly as an input to the xsl policy )

<Source>newlyCreatedMessage</Source>

Otherwise everything works as expected.

Just curious to know why does the 'newlyCreatedMessage' variable in the Assign Message policy doesn't get populated with the input xml payload from the KVM in the trace , but works fine as an input to the XSLT policy ?

I think that might be a bug in the Trace facility in Edge!

I will investigate.

Update: it's a bug; I've logged APIRT-3546 to track this. The basic problem is that when AssignMessage is used with AssignTo/@createNew=true , appropriate information is not logged. Therefore the Trace screen cannot display anything useful about the step.

Hi Dino, the variable references in the curly braces in the XML payload (body) is not working for me correctly. any ideas ?

Are you trying with template tag? If not try that. You can also try with javascript.