0

It should return all first occurrences for nodes with same name, even if the childs and attribbutes differ.

For example

<Data>
  <A>
    <X randomattr="1"/>
    <Y randomattr="1"/>
    <Z/>
  </A>
  <B>
    <X/>
    <X randomattr="3"/>
    <Z/>
  </B>
</Data>

It sould return 3 nodes, first X, Y and Z, because the following, will have a repeated name. Don't mind if one of the X elements does not have randomattr or if another has a different value.

I dont want the distinct-values from name(), I want to return the whole node. Something like

/Data/*/*[distinct-values(name())]

I also know I can transverse all nodes with a double loop, but I am asking myyself if there is an easy one-liner or a function for this, or a special Xpath syntax like distinct[1] Thank u!

Whimusical
  • 6,401
  • 11
  • 62
  • 105
  • 1
    Your going to have to explain this a bit better. Are you saying you want the A node, the children of the A node. The first node of each name at the same level as A, the children of the above??? – Tony Hopkinson Jul 12 '12 at 13:10
  • I want all the nodes in second level but just the first when there are multiples with the same name – Whimusical Jul 12 '12 at 16:57
  • 1
    I don't know of any way of doing that in Xpath. You could use xslt to do it and then query that. something like. http://stackoverflow.com/questions/1791108/xpath-expression-to-select-all-xml-child-nodes-except-a-specific-list . Might be worth closing this question, and asking another, basically you are looking for something like node_name() In (listofnames). Gettiong more specific might get you somewhere. – Tony Hopkinson Jul 13 '12 at 13:06
  • I just want to return the set of the first occurrences for each second-level element. Occurrence = same element name. I know i explained bad, but is realy simple the idea. – Whimusical Jul 13 '12 at 13:55
  • Ok, I Did it! Excuse me, I thought it was enough for understanding the Xpath syntax i was looking for – Whimusical Jul 16 '12 at 18:57

2 Answers2

0

I think this is what you might be looking for. Worked with some simple tests:

  • (Xpath 2.0) /Data/*/*[not((preceding-sibling::*/name(.)=name(.)) or (../preceding-sibling::*/*/name(.)=name(.)))]
  • (Xpath 1.0) /Data/*/*[not((name(preceding-sibling::*)=name(.)) or (name(../preceding-sibling::*/*)=name(.)))]

After the request to make it work to any depth... I am unfamiliar with your schema, but from the example I'm assuming that the nodes you are looking for are the bottom level nodes. The following uses the descendant axes to search for nodes with no children while doing the name check.

  • /Data/descendant::*[not(name() = preceding::*[not(child::*)]/name())][not(child::*)]
AlwaysWrong
  • 526
  • 4
  • 9
  • It doesn't work if you have multiple instances of the same element per parent (I mean the XPath 1.0 version) – toniedzwiedz Jul 16 '12 at 19:32
  • Thanks!! Very useful!! A problem is that the sample was simple and has only 2 deep levels, but what if in between Data and A theres 2 or 3 more hierarchy levels? You should write a preceding-sibling clausule for any level, growing complex. Any way to solve this regardless of deep levels of sibling – Whimusical Jul 17 '12 at 09:07
0

I will accept alwaysWrong question, but I answer another possibility from what I learned from him and after some researching with AXES thing I just learnt.

/Data/*/*[not(name() = preceding::*[name(../..)='Data']/name())]

(First name() could be also self::*/name() following syntax)

This would be the easier and best line I think, for any context, since it does not grow exponentially complex when the leaf-level is deep. Just change the condition on precedents to match 'from-root' siblings like [name(../..)='Data'] or a condition that defines all nodes of this hierarchy level, or preceding::name_node if they al share same qname

Whimusical
  • 6,401
  • 11
  • 62
  • 105