0

I can’t figure out how to select the first child of a grandparent.

Simple structure:

<root>
    <front>
        <div>
            <head>a</head>
            <head>1</head>
        </div>
        <div>
            <head>b</head>
            <head>2</head>
        </div>
    </front>
    <body>
        <div>
            <head>c</head>
            <head>3</head>
        </div>
        <div>
            <head>d</head>
            <head>4</head>
        </div>
    </body>
    <back>
        <div>
            <head>e</head>
            <head>5</head>
        </div>
        <div>
            <head>f</head>
            <head>6</head>
        </div>
    </back>
</root>

How to select head e with the help of axes? I need that for testing inside an XSLT stylesheet, hence the emphasis on axes.

The selection //head[parent::*[self::div]][1][ancestor::back] doesn’t work, it still selects both, e and f.

Honza Hejzl
  • 874
  • 8
  • 23
  • 1
    what is the context element when you say "*the path .... it still selects both, **e** and **f***" ? – har07 Feb 24 '16 at 12:09
  • 1
    @HonzaHejzl That makes no sense: which head? And what version of XPath? – michael.hor257k Feb 24 '16 at 12:13
  • The head **e** is the context node. (As for XPath version, I have no idea, I am simply using the latest stable releases [in XML Oxygen and eXist-db].) – Honza Hejzl Feb 24 '16 at 12:57
  • @HonzaHejzl See here how to find out: http://stackoverflow.com/questions/25244370/how-can-i-check-which-xslt-processor-is-being-used-in-solr/25245033#25245033 Please make it a habit to state your version when asking about XSLT or XPath. – michael.hor257k Feb 24 '16 at 13:09

3 Answers3

0

From the context of root, the following XPath 2.0 expression will select the the first head grandchild of back:

back/(div/head)[1]

To do the same in XPath 1.0, try:

back/div[head][1]/head[1]

Edit:

I have the head selected by a stylesheet, now I need to test whether it is the first head of the first div in the back section.

That's a completely different question. From the context of any head you can select the first head of the first div of the current grandparent by:

../../div[1]/head[1]

Note that this assumes (as does your question) that the first div does have a head child.

To restrict the scope to only the back grandparent, you could use:

ancestor::back/div[1]/head[1]
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • I am sorry, I need that for iteration over `head` element chosen with an XSLT template. That means, I need to use axes, not direct selection downwards. – Honza Hejzl Feb 24 '16 at 12:26
  • I am afraid I have no idea what that means. Every selection uses axes. If you're in the context of root, then the axis can only be child or descendant. – michael.hor257k Feb 24 '16 at 12:42
  • It is simple, try to do the same starting from the head element upwards. From the top to the bottom it is really simple but in the other direction there are some glitches. I have the head selected by a stylesheet, now I need to test whether it is the first head of the first div in the back section. (I think you know [this](http://www.w3schools.com/xsl/xpath_axes.asp).) – Honza Hejzl Feb 24 '16 at 12:48
  • I am sorry, misunderstanding. The context node (in the term of my stylesheet **is** the head **e**, which I mentioned in the question. – Honza Hejzl Feb 24 '16 at 12:55
  • @HonzaHejzl I have added an answer to your (new) question. However, I suspect this is an [XY problem](http://xyproblem.info/). – michael.hor257k Feb 24 '16 at 13:15
  • Thank you for the moral punch, you are probably right. Sometimes it is a bit difficult to name something properly, mainly when the problem includes more than one technology. I will remember the lesson well. My new answer solves the question. If you would recommend improvements for better readability of the initial question, don’t hesitate and let me know, I want to produce good content here. – Honza Hejzl Feb 24 '16 at 14:29
0

Assuming the context element is <head>f</head>, the following should work:

If you are looking for the first div child of grandparent

../../div[1]

If you are looking for any child of grandparent

../../*[1]
user3392439
  • 973
  • 8
  • 6
0

The working solution is a bit tricky (XPath 2.0):

//head[count(parent::div[preceding-sibling::div]) < 1 and ancestor::back][1]

In the XSLT stylesheet works for me (XSLT 1.0):

test=".[count(parent::div[preceding-sibling::div]) &lt; 1 and ancestor::back][1]"

UPDATE

As shown in comments, there is another one solution, more suitable for XSLT:

test=". is ancestor::back/div[1]/head[1]"
Honza Hejzl
  • 874
  • 8
  • 23
  • **1.** That cannot work in XSLT, because you have an unescaped `<`. **2.** I believe the test returns true for both `e` and `5` - which is not what you asked for. – michael.hor257k Feb 24 '16 at 14:57
  • **1.** Ok, changed to `<` in the case for XSLT. **2.** Tested in Oxygen Author 17 as well as XSLT stylesheets for a project generating journal articles from TEI XML, still works ([screen from Oxygen](ftp://46.28.111.241/xpath-sample.jpg)). – Honza Hejzl Feb 24 '16 at 15:32
  • I am not sure what your screenshot is supposed to prove. I suggest you compare http://xsltransform.net/94rmq6Q/1 to http://xsltransform.net/94rmq6Q – michael.hor257k Feb 25 '16 at 07:33
  • 1
    @michael.hor257k, thanks for nice examples (I didn’t know the app). I have two things I want to mention: **1.** I can’t help, my tricky solution still works no matter how hard I try. _Your_ solution is much much better and works for XSLT (but I am not able to reproduce that in Oxygen—that is why I didn’t do it the same way at first). Thanks for that, I will update the answer and mark it. **2.** You seem nicely skilled but too much strict. I don’t want to win something, I am just relatively new to Stack and need some time for learning how to participate. Please, have that on mind. – Honza Hejzl Feb 25 '16 at 08:19
  • **1.** Please refrain from personal remarks. **2.** I am trying to help you the best way I can. It is much more difficult to do when I don't know your purpose. **3.** Try to understand that my time here is limited. I am not trying to win anything, but I may be terse. – michael.hor257k Feb 25 '16 at 08:44