Problem with XPath

johnferguson
Participant II

I'm trying to use XPath to extract some values from an XML Response Payload. I have validated the XPath elsewhere, but for some reason, it doesn't appear to be working at runtime in Apigee.

XML Payload:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="urn:xtk:session" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <SOAP-ENV:Body>
      <LogonResponse xmlns="urn:xtk:session" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <pstrSessionToken xsi:type="xsd:string">___xxx</pstrSessionToken>
         <pSessionInfo xsi:type="ns:Element" SOAP-ENV:encodingStyle="http://xml.apache.org/xml-soap/literalxml">
            <sessionInfo>
               <serverInfo advisedClientBuildNumber="xx" allowSQL="false" buildNumber="xx" databaseId="xxx" defaultNameSpace="xxx" instanceName="xxx" majNumber="6" minClientBuildNumber="xx" minNumber="0" minNumberTechnical="0" securityTimeOut="xx" serverDate="xxx" servicePack="1" sessionTimeOut="xxx" />
               <userInfo datakitInDatabase="true" homeDir="" instanceLocale="en-US" locale="en-US" login="xxx" loginCS="xxx" loginId="xxx" noConsoleCnx="false" orgUnitId="0" theme="" timezone="Europe/London">
                  <login-group id="xxx" />
                  <login-right right="admin" />
                  <installed-package name="core" namespace="acx" />
               </userInfo>
            </sessionInfo>
         </pSessionInfo>
         <pstrSecurityToken xsi:type="xsd:string">@xxx==</pstrSecurityToken>
      </LogonResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The Extract Variable Policy:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables async="false" continueOnError="false" enabled="true" name="EV-ExtractAdobeSessionID">
    <DisplayName>EV-ExtractAdobeSessionID</DisplayName>
    <Source clearPayload="false">adobe.logonResponse.content</Source>
    <VariablePrefix>adobe</VariablePrefix>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <XMLPayload stopPayloadProcessing="true">
        <Namespaces>
            <Namespace prefix="ns">urn:xtk:session</Namespace>
            <Namespace prefix="xsd">http://www.w3.org/2001/XMLSchema</Namespace>
            <Namespace prefix="xsi">http://www.w3.org/2001/XMLSchema-instance</Namespace>
            <Namespace prefix="SOAP-ENV">http://schemas.xmlsoap.org/soap/envelope</Namespace>
        </Namespaces>
        <Variable name="session" type="string">
            <XPath>/SOAP-ENV:Envelope/SOAP-ENV:Body/LogonResponse/pstrSessionToken/text()</XPath>
        </Variable>
        <Variable name="security" type="string">
            <XPath>/SOAP-ENV:Envelope/SOAP-ENV:Body/LogonResponse/pstrSecurityToken/text()</XPath>
        </Variable>
    </XMLPayload>
</ExtractVariables>

I would appreciate any help in figuring out why my XPath isn't resolving.

Please note:

  • The XML payload is provided by a 3rd party API, so I can't change it
  • I have confirmed the abobe.logonResponse.content variable contains the full XML payload
  • The EV policy does not fail, it just doesn't populate adobe.session or adobe.security as expected
Solved Solved
1 4 1,773
1 ACCEPTED SOLUTION

ok, I see three problems.

  1. The soap namespace in the response document is http://schemas.xmlsoap.org/soap/envelope/ . Note the trailing slash. In your policy configuration, you have

    <Namespace prefix="SOAP-ENV">http://schemas.xmlsoap.org/soap/envelope</Namespace>
    	

    There is no trailing slash here. That means it is a totally different namespace. Add the slash to the namespace declaration in the policy configuration, to fix this problem.

  2. The LogonResponse element in the response document actually is qualified by an XML namespace. The actual syntax in the document you showed is:
          <LogonResponse xmlns="urn:xtk:session"
    	

    That xmlns='...' thing says "this element falls within this namespace. That means if you use an xpath to refer to that element, you need to specify the namespace prefix. But your xpath lacked a prefix for this element. Likewise for the pstrSessionToken element. You had this:

          <XPath>/SOAP-ENV:Envelope/SOAP-ENV:Body/LogonResponse/pstrSessionToken/text()</XPath>
    	

    you want this:

          <XPath>/SOAP-ENV:Envelope/SOAP-ENV:Body/ns:LogonResponse/ns:pstrSessionToken/text()</XPath>
    	

    And you need to make the similar change for the second XPath.

  3. Finally, a problem that you didn't run into, but you will if you fix the prior two. You use stopPayloadProcessing="true". According to the documentation, that tells the ExtractVariables policy to stop populating variables after the first successful one. If you want to extract both the session and the security token, you need to remove that, or set it to false.

Also, in case you were not aware, the XML namespace prefixes that you use in xpath are not syntactically important. The important thing is the referent of the prefix. You can use a different prefix in your xpath, than appears in the actual XML document, as long as the respective prefixes refer to the same xml namespace string (even down to the last trailing slash!). Also, while the namespaces with prefixes like xsi and xsd are present in the original document, those namespaces are not used by either of your xpaths. So you can omit them from the policy configuration. With all of those changes, this policy configuration, where I use soap as the prefix instead of SOAP-ENV for the soap namespace, works fine:

 <ExtractVariables name='EV-ExtractAdobeSessionID'>
  <Source>my_message_variable</Source>
  <VariablePrefix>adobe</VariablePrefix>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <XMLPayload>
    <Namespaces>
      <Namespace prefix="ns">urn:xtk:session</Namespace>
      <Namespace prefix="soap">http://schemas.xmlsoap.org/soap/envelope/</Namespace>
    </Namespaces>
    <Variable name="session" type="string">
      <XPath>/soap:Envelope/soap:Body/ns:LogonResponse/ns:pstrSessionToken/text()</XPath>
    </Variable>
    <Variable name="security" type="string">
      <XPath>/soap:Envelope/soap:Body/ns:LogonResponse/ns:pstrSecurityToken/text()</XPath>
    </Variable>
  </XMLPayload>
</ExtractVariables>

I cannot show you my results, because for some reason the image upload widget is not working today. But trust me, it's working !

--

You said that your xpath worked fine outside of Apigee Edge. I think maybe you have used a slightly different configuration - different namespaces and maybe different prefixes - if that is the case. For sure, the suggestions and corrections I offered above are standard XPath and XML Namespace things; they are not peculiar to Apigee Edge. A compliant XPath evaluator would need the same adjustments.

If you have any questions about any of this, let me know!

View solution in original post

4 REPLIES 4

John, THANK YOU! For formatting your code and being clear about what you are seeing, and what you are expecting to see. This makes it super easy to help you!

ok, I see three problems.

  1. The soap namespace in the response document is http://schemas.xmlsoap.org/soap/envelope/ . Note the trailing slash. In your policy configuration, you have

    <Namespace prefix="SOAP-ENV">http://schemas.xmlsoap.org/soap/envelope</Namespace>
    	

    There is no trailing slash here. That means it is a totally different namespace. Add the slash to the namespace declaration in the policy configuration, to fix this problem.

  2. The LogonResponse element in the response document actually is qualified by an XML namespace. The actual syntax in the document you showed is:
          <LogonResponse xmlns="urn:xtk:session"
    	

    That xmlns='...' thing says "this element falls within this namespace. That means if you use an xpath to refer to that element, you need to specify the namespace prefix. But your xpath lacked a prefix for this element. Likewise for the pstrSessionToken element. You had this:

          <XPath>/SOAP-ENV:Envelope/SOAP-ENV:Body/LogonResponse/pstrSessionToken/text()</XPath>
    	

    you want this:

          <XPath>/SOAP-ENV:Envelope/SOAP-ENV:Body/ns:LogonResponse/ns:pstrSessionToken/text()</XPath>
    	

    And you need to make the similar change for the second XPath.

  3. Finally, a problem that you didn't run into, but you will if you fix the prior two. You use stopPayloadProcessing="true". According to the documentation, that tells the ExtractVariables policy to stop populating variables after the first successful one. If you want to extract both the session and the security token, you need to remove that, or set it to false.

Also, in case you were not aware, the XML namespace prefixes that you use in xpath are not syntactically important. The important thing is the referent of the prefix. You can use a different prefix in your xpath, than appears in the actual XML document, as long as the respective prefixes refer to the same xml namespace string (even down to the last trailing slash!). Also, while the namespaces with prefixes like xsi and xsd are present in the original document, those namespaces are not used by either of your xpaths. So you can omit them from the policy configuration. With all of those changes, this policy configuration, where I use soap as the prefix instead of SOAP-ENV for the soap namespace, works fine:

 <ExtractVariables name='EV-ExtractAdobeSessionID'>
  <Source>my_message_variable</Source>
  <VariablePrefix>adobe</VariablePrefix>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <XMLPayload>
    <Namespaces>
      <Namespace prefix="ns">urn:xtk:session</Namespace>
      <Namespace prefix="soap">http://schemas.xmlsoap.org/soap/envelope/</Namespace>
    </Namespaces>
    <Variable name="session" type="string">
      <XPath>/soap:Envelope/soap:Body/ns:LogonResponse/ns:pstrSessionToken/text()</XPath>
    </Variable>
    <Variable name="security" type="string">
      <XPath>/soap:Envelope/soap:Body/ns:LogonResponse/ns:pstrSecurityToken/text()</XPath>
    </Variable>
  </XMLPayload>
</ExtractVariables>

I cannot show you my results, because for some reason the image upload widget is not working today. But trust me, it's working !

--

You said that your xpath worked fine outside of Apigee Edge. I think maybe you have used a slightly different configuration - different namespaces and maybe different prefixes - if that is the case. For sure, the suggestions and corrections I offered above are standard XPath and XML Namespace things; they are not peculiar to Apigee Edge. A compliant XPath evaluator would need the same adjustments.

If you have any questions about any of this, let me know!

Dino,

Many thanks for your detailed response. It did the trick!

The trailing space was just a copy and paste error when I brought the code into here, but adding ns: prefix in the additional nodes in the XPath solved the problem.

The XPath validator I was using was here:

https://www.freeformatter.com/xpath-tester.html

It does seem to return the correct value without the need for the ns prefix on the latter two nodes.

Ahh, yes... Glad to be of assistance.

That online tool is subtly broken, and seems to have lead you astray!