2

I have this file, which have namespaces in it, I am trying to write a Xquery which will return, the last node having the namespace, but unabe to find the logic to return the namespace node(PS: I am new to Xquery)

XML File -

<XML>
  <SOAPMessage>
    <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/">
      <soapenv:Body>
        <postRequestResponse    xmlns="http://company.com">
          <postRequestReturn>
            <OnlineBRE  xmlns="">
              <Proccode>NUM</Proccode>
            </OnlineBRE>
          </postRequestReturn>
        </postRequestResponse>
      </soapenv:Body>
    </soapenv:Envelope>
  </SOAPMessage>
</XML>

  

I have tried a query to return node with exact string value, but that didn't work.

Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • While asking an XQuery question you need to provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example): (1) Input XML. (2) Your logic, and XQuery that tries to implement it. (3) Desired output based on the #1 above. (4) XQuery processor and its conformance with the XQuery standards: 1.0, 3.0, 3.1, or 4.0. – Yitzhak Khabinsky Jan 06 '23 at 18:24

2 Answers2

2

You can look for all elements in the document //* and test whether there is a namespace-uri() in a predicate filter, then select the last() element that has a namespace-uri().

let $doc := 
  <XML>
    <bk:BOOK xmlns:bk="urn:example.microsoft.com:BookInfo" xmlns:money="urn:Finance:Money">
      <bk:TITLE>value1</bk:TITLE>
      <ONLINEBRE />
      <bk:PRICE money:currency="US Dollar">22.95</bk:PRICE>
    </bk:BOOK>
  </XML>
return ($doc//*[namespace-uri()])[last()]
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • This will return all the elements after the last namespace element, how to get the value of the node that have the last namespace, is it a way in Xquery to get the path of the node with the last namespace @MadsHansen – Adarsh Singh Jan 06 '23 at 19:33
  • 1
    I don't understand what you mean. Are you asking for the parent of that element? It's easier if you indicate what you actually want, provide an example from your sample XML. – Mads Hansen Jan 06 '23 at 19:52
  • I have entered the exact xml for which I needed the help, Mads, in this since the last one to have the namespace is ONLINEBRE, so i want the answer to be only "ONLINEBRE", or the XPATH of the tag – Adarsh Singh Jan 06 '23 at 20:24
  • 1
    that element **doesn't** have a namespace. You said you wanted to select the last element that **has a namespace**. So, if you want to select the `ONLINEBRE`, what is the logic? – Mads Hansen Jan 06 '23 at 20:44
  • I want the value of the tag that has the last namespace, lets say i want the answer to be – Adarsh Singh Jan 06 '23 at 20:50
  • I have added the new XML – Adarsh Singh Jan 06 '23 at 20:51
  • `postRequestReturn` is bound to the same namespace as `postRequestResponse`, so it's actually the last element that has a namespace. You are looking for the last element that has `@xmlns:*` ?? That's not a normal attribute, so you can't select it via XPath like you can with "real" attributes. Still trying to understand what you are looking to achieve and why. – Mads Hansen Jan 06 '23 at 22:34
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/250918/discussion-between-adarsh-singh-and-mads-hansen). – Adarsh Singh Jan 07 '23 at 04:51
  • I dont want the value but the element name, for example in your first comment, I want the answer to be – Adarsh Singh Jan 07 '23 at 09:39
1

Based on your comments on the Mads Hansen's answer I came up with this:

declare namespace array = "http://www.w3.org/2005/xpath-functions/array";

declare function local:ns-changed ($current as element(), $last-ns, $last-element) {
  $current/element() ! (
    let $child-ns := namespace-uri-from-QName(node-name(.))
    return
      if ( $child-ns = $last-ns )
      then local:ns-changed(., $last-ns, $last-element)
      else local:ns-changed(., $child-ns, .)
  ),
  $last-element
};

let $root :=
<XML>
  <SOAPMessage>
    <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/">
      <soapenv:Body>
        <postRequestResponse    xmlns="http://company.com">
          <postRequestReturn>
            <OnlineBRE  xmlns="">
              <Proccode>NUM</Proccode>
            </OnlineBRE>
          </postRequestReturn>
        </postRequestResponse>
      </soapenv:Body>
    </soapenv:Envelope>
  </SOAPMessage>
</XML>

let $initial-ns := namespace-uri-from-QName(node-name($root))
let $wanted := head(local:ns-changed($root, $initial-ns, $root))

return $wanted

I may not be pretty and inefficient and also only tested on your provided input, but it does what you want.

Here you can see it in action https://xqueryfiddle.liberty-development.net/3Nzd8c9/1

Currently it returns the last element where the namespace changed. To return the node-name or the value or something else modify the last line.

line-o
  • 1,885
  • 3
  • 16
  • 33