How to extract variable from XML Payload with namespace declaration?

I have a proxy that receives a xml payload of this format

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
	<SOAP-ENV:Header>
		<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
			<wsse:UsernameToken>
				<wsse:Username>string</wsse:Username>
				<wsse:Password>string</wsse:Password>
				<wsse:Nonce EncodingType="string">string</wsse:Nonce>
				<wsu:Created>string</wsu:Created>
			</wsse:UsernameToken>
			<wsu:Timestamp wsu:Id="string">
				<wsu:Created>string</wsu:Created>
				<wsu:Expires>string</wsu:Expires>
			</wsu:Timestamp>
		</wsse:Security>
	</SOAP-ENV:Header>
	<SOAP-ENV:Body>
		<GetXdata_IN xmlns="com.abc.def.getxdata">
			<Code>K</Code>
		</GetXdata_IN>
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

  I'm using the EV policy to extract the <Code>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables async="false" continueOnError="false" enabled="true" name="EV-Code">
    <DisplayName>EV-Code</DisplayName>
    <Source>request</Source>
    <XMLPayload stopPayloadProcessing="false">
        <Namespaces>
            <Namespace prefix="ns0">com.abc.def.getxdata</Namespace>
        </Namespaces>
        <Variable name="Code" type="string">
            <XPath>//ns0:GetXdata_IN/ns0:Code</XPath>
        </Variable>
    </XMLPayload>
</ExtractVariables>




But this breaks when I receive the XML with namespace like

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
	<SOAP-ENV:Header>
		<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
			<wsse:UsernameToken>
				<wsse:Username>string</wsse:Username>
				<wsse:Password>string</wsse:Password>
				<wsse:Nonce EncodingType="string">string</wsse:Nonce>
				<wsu:Created>string</wsu:Created>
			</wsse:UsernameToken>
			<wsu:Timestamp wsu:Id="string">
				<wsu:Created>string</wsu:Created>
				<wsu:Expires>string</wsu:Expires>
			</wsu:Timestamp>
		</wsse:Security>
	</SOAP-ENV:Header>
	<SOAP-ENV:Body>
		<GetXdata_IN xmlns_tns="com.abc.def.getxdata">
			<Code>K</Code>
		</GetXdata_IN>
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

How do I manage to read the <Code> element irrespective of the namespace /prefix

Like I could get it as
<GetXdata_IN xmlns_tns="com.abc.def.getxdata">
OR
<GetXdata_IN xmlns_tnsinput="com.abc.def.getxdata">
@dchiesa1 
Please advise.

Solved Solved
0 2 1,248
1 ACCEPTED SOLUTION

Yes. Though we all may not like it, the rules around XML namespaces are clearly defined and strict. The upshot is that this:

 

<GetXdata_IN xmlns="com.abc.def.getxdata">

 

..is intended to convey a different meaning than this:

 

<GetXdata_IN xmlns_tns="com.abc.def.getxdata">

 

...and the various XML tools and technologies that support XML namespaces, including XPath, recognize that fact. The former is an element in the com.abc.def.getxdata namespace. The latter is an element in the "default" namespace, which... is determined by the parent hierarchy. In your example the default namespace for that element was -none-.

BTW, this statement of yours is not sensible:

when I receive the XML with namespace like

You were referring to the latter XML fragment I showed above. The attribute xmlns_tns does not convey a namespace. It has no meaning, w.r.t. XML namespaces. It looks like it could be a namespace declaration. It looks pretty similar. But it's not a namespace declaration, so you are not actually "receiving" the namespace.

I think you could use something like this as your expression, to focus just on the element name, and ignore the namespace altogether:

 

<ExtractVariables async="false" continueOnError="false" enabled="true" name="EV-Code">
    <DisplayName>EV-Code</DisplayName>
    <Source>request</Source>
    <XMLPayload stopPayloadProcessing="false">
        <Namespaces>
            <Namespace prefix="soap">http://schemas.xmlsoap.org/soap/envelope/</Namespace>
        </Namespaces>
        <Variable name="Code" type="string">
            <XPath>//soap:Body/*[local-name()='GetXdata_IN']/*[local-name()='Code']/text()</XPath>
        </Variable>
    </XMLPayload>
</ExtractVariables>

 

That will "work" for the scenario you described but it feels unhygienic to me. The real problem is if you have a downstream system that is generating XML that can take either of those two forms that you mentioned, something is broken there. It shouldn't do that. I would want to fix the problem there, rather than configure Apigee to adapt to the inconsistency. I hope this is clear.

 

View solution in original post

2 REPLIES 2

You can extract data from xml payload , u need to declare the Namespace and access the xml using the declared Namsapce.
You can see the Below Extract Variable Policy will Extract Code value 'K'  from any prefix name for namespace , just declare the name space and use the declared namespace prefix in the XPATH.

 

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables async="false" continueOnError="false" enabled="true" name="EV-Code">
    <DisplayName>EV-Code</DisplayName>
    <Source>request</Source>
    <XMLPayload stopPayloadProcessing="false">
        <Namespaces>
            <Namespace prefix="ns0">com.abc.def.getxdata</Namespace>
            <Namespace prefix="soap">http://schemas.xmlsoap.org/soap/envelope/</Namespace>
        </Namespaces>
        <Variable name="Code" type="string">
            <XPath>//soap:Body/GetXdata_IN/Code</XPath>
        </Variable>
    </XMLPayload>
</ExtractVariables>

 

 

 Also as alternative if you have issues with Namespace use XSLT Transform Policy and Remove the Namespace and try .
Or You can use XMLtoJson and  use the converted json if you have issue with XML.
Your Second XML Payload line

<GetXdata_IN xmlns_tns="com.abc.def.getxdata">

is Wrong bcz the actual declaration should be like this xmlns:prefix.
if you have still issue try to use remove-namspace xsl transformation and then extract it will work


Yes. Though we all may not like it, the rules around XML namespaces are clearly defined and strict. The upshot is that this:

 

<GetXdata_IN xmlns="com.abc.def.getxdata">

 

..is intended to convey a different meaning than this:

 

<GetXdata_IN xmlns_tns="com.abc.def.getxdata">

 

...and the various XML tools and technologies that support XML namespaces, including XPath, recognize that fact. The former is an element in the com.abc.def.getxdata namespace. The latter is an element in the "default" namespace, which... is determined by the parent hierarchy. In your example the default namespace for that element was -none-.

BTW, this statement of yours is not sensible:

when I receive the XML with namespace like

You were referring to the latter XML fragment I showed above. The attribute xmlns_tns does not convey a namespace. It has no meaning, w.r.t. XML namespaces. It looks like it could be a namespace declaration. It looks pretty similar. But it's not a namespace declaration, so you are not actually "receiving" the namespace.

I think you could use something like this as your expression, to focus just on the element name, and ignore the namespace altogether:

 

<ExtractVariables async="false" continueOnError="false" enabled="true" name="EV-Code">
    <DisplayName>EV-Code</DisplayName>
    <Source>request</Source>
    <XMLPayload stopPayloadProcessing="false">
        <Namespaces>
            <Namespace prefix="soap">http://schemas.xmlsoap.org/soap/envelope/</Namespace>
        </Namespaces>
        <Variable name="Code" type="string">
            <XPath>//soap:Body/*[local-name()='GetXdata_IN']/*[local-name()='Code']/text()</XPath>
        </Variable>
    </XMLPayload>
</ExtractVariables>

 

That will "work" for the scenario you described but it feels unhygienic to me. The real problem is if you have a downstream system that is generating XML that can take either of those two forms that you mentioned, something is broken there. It shouldn't do that. I would want to fix the problem there, rather than configure Apigee to adapt to the inconsistency. I hope this is clear.