3

I am looking into the XPath Axes and I am able to understand all below axes

'ancestor'
| 'ancestor-or-self'
| 'attribute'
| 'child'
| 'descendant'
| 'descendant-or-self'
| 'following'
| 'following-sibling'
| 'parent'
| 'preceding'
| 'preceding-sibling'
| 'self'

The only axes I am not able to understand is

| 'namespace'

Can anyone give me a good example and understanding about what 'namespace' actually do?

Example:-

Open :- https://www.google.co.in/

OR

HTML code

<a style="left:-1000em;position:absolute" href="/setprefs?suggon=2&prev=https://www.google.co.in/&sig=0_ujdR1PrGxEbi_EiD6RbIb4VvaXc%3D">Screen-reader users, click here to turn off Google Instant.</a>

I am trying the below xpath

  //a[@style='left:-1000em;position:absolute']/namespace::*[name()='google']

What is wrong I am doing in above namespace?

In addition I also want to know about that what is the use of axes | 'attribute'. where and in which situation it is helpful

Shubham Jain
  • 16,610
  • 15
  • 78
  • 125

2 Answers2

4

namespace:: axis selects namespace nodes. Nothing fancy.

And namespace nodes, according to the linked specs includes :

  • every attribute on the element whose name starts with xmlns:

  • every attribute on an ancestor element whose name starts xmlns: unless the element itself or a nearer ancestor redeclares the prefix

  • an xmlns attribute, if the element or some ancestor has an xmlns attribute, and the value of the xmlns attribute for the nearest such element is non-empty

For example, given the following XML element :

<a xmlns:google="some namespace uri here"/>

XPath expression below will return the xmlns:google attribute [demo]. :

//a/namespace::*[name()='google']

This example corresponds to the first bullet point mentioned above, since xmlns:google is attribute, on the context element <a>, whose name starts with xmlns:.


Similar explanation goes for attribute axis; it simply selects XML attributes.

You can consider attribute axis as a longer version of @ which you've already been using. For example, the two expressions below mean the same :

//a[attribute::style='left:-1000em;position:absolute']
//a[@style='left:-1000em;position:absolute']
har07
  • 88,338
  • 12
  • 84
  • 137
  • Could you provide similar example with `namespace::`? When compared to attributes, there is a problem because it is possible to declare namespace in two basic ways (in a parent element, as an attribute of the current element). People often think it is usable for selecting of particular elements **in** a particular namespace. It could be convenient but I think there is no way how to do that. – Honza Hejzl Mar 16 '16 at 13:47
  • *"Could you provide similar example with namespace::?"*. Do you mean example that filter element by namespace value/URI, like this : `//a[namespace::*='some namespace uri here']` ? – har07 Mar 17 '16 at 05:37
  • I have tried the same in my local but it is not working .. I have added in my HTML and try to search it by //a/namespace::*[name()='google'] .. the result is empty – Shubham Jain Mar 17 '16 at 07:16
  • @ShubhamJain Maybe the XPath processor you used doesn't implement `namespace` axis? http://stackoverflow.com/a/11189061/2998271 – har07 Mar 17 '16 at 07:28
  • ya may be .. because I am trying many different combination with namespace and it is not working for me.. Thanks for the help – Shubham Jain Mar 17 '16 at 07:33
  • Yes, I mean using the `namespace::` axis in an element’s predicate. For me it does not work either. I am testing in Oxygen Author 17.1, XPath 1.0, XPath 2.0. @har07, could you describe your technical scenario, please? – Honza Hejzl Mar 17 '16 at 08:10
  • @HonzaHejzl I tested in xpathtester.com : http://www.xpathtester.com/xpath/ae2d285ae84b292608620462db71b7a1 . I *guess*, the site is using Java XPath engine, maybe Saxon – har07 Mar 17 '16 at 08:16
  • Thank you, I can confirm it works. However, it works **only** if the namespace is declared directly for the element, as an attribute. If it is declared in the root element as a default namespace or if it is declared in the root and used as a prefix, it does not work. I think there is no clean solution. The funny thing is the initial question is a typical [XY problem](http://xyproblem.info/), we are trying to answer to Y but X was needed. In spite of this, useful question. – Honza Hejzl Mar 17 '16 at 08:26
  • Still worked when the namespace declared at the root element : http://www.xpathtester.com/xpath/ed8fb2ec5c3c0478d9250005f3c412ca . Maybe I misunderstand the scenario which didn't work as per your comment – har07 Mar 17 '16 at 08:52
  • What about [this](http://www.xpathtester.com/xpath/04a6547e2cf824e411d3ae7780a90fa3)? Please do not consider this as a game, I only want to shape the answer as properly as possible. – Honza Hejzl Mar 17 '16 at 09:24
  • 1
    @HonzaHejzl The reason why the XPath you linked to yielded no result has nothing to do with the `namespace` in predicate. It is because `` inherits default namespace now, so even just `//a` would have returned no result. Compare your XPath with this one : `//*[local-name()='a' and namespace::*='some namespace uri here']` – har07 Mar 17 '16 at 10:40
  • 1
    Could I use that in the answer? Just for the sake of completeness? You are right, thanks, nice lesson! – Honza Hejzl Mar 17 '16 at 14:24
  • No problem, feel free to include that in your answer – har07 Mar 17 '16 at 14:43
2

According to the logic, it should select all elements associated with the namespace. However, its behavior is a bit more complicated, hence it is not used often. (It is considered as deprecated in XPath 2.0.)

<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
    <BASE xmlns:base="http://www.tei-c.org/ns/1.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
        <base:p>XXX</base:p>
        <dc:p>yyy</dc:p>
    </BASE>
    <NEXT xmlns="http://purl.org/dc/elements/1.1/">
        <p>zzz</p>
    </NEXT>
</ROOT>

XPath (2.0):

//*[namespace::dc]

selects the whole BASE element. It is similar to:

//*[namespace-uri-for-prefix('dc', .)]

which does not work in XPath 1.0.

//*[namespace::*='http://purl.org/dc/elements/1.1/']

selects both, BASE as well as NEXT elements. Such a usage is considered a bit strange and it is not recommended.

//*[local-name(.) eq 'p' and namespace::*='http://purl.org/dc/elements/1.1/']

selects all p elements, which means base:p, dc:p, p as well.

//*[local-name(.) eq 'p' and namespace::dc]

selects base:p and dc:p of the BASE element.

//*[local-name(.) eq 'p' and namespace-uri() eq 'http://purl.org/dc/elements/1.1/']

selects all elements bounded to the URI. It means it is the most accurate option.

Or it is possible to try something like:

//*[contains(namespace-uri(), 'purl')]

or

//self::BASE/*[contains(namespace-uri(), 'purl')]

Tested in Oxygen Author 17.1

UPDATE

As for your need, it seems you are probably trying to select an element with the string google in the href attribute, not with a particular namespace. What about //a[contains(@href, 'google')]?

UPDATE II

As stated in comments, the UPDATE was the working solution. However, I think the next information is also useful:

Honza Hejzl
  • 874
  • 8
  • 23