Which should I use: preceding:: or preceding-sibli

2020-03-26 07:41发布

I'm hesitating whether to write preceding or preceding-sibling in XSL such as an example xml below.

<sales_division>
  <team area="Tokyo">
    <staff>Sato</staff>
    <staff>Tanaka</staff>
  </team>
  <team area="Osaka">
    <staff>Ueda</staff>
    <staff>Suzuki</staff>
  </team>
</sales_division>

If current node is team(@Osaka), and I want to get first-child node staff(Sato) of team(@Tokyo), what should I write Xpath appropriately?

I guess three options below.

1) preceding-sibling::team/staff[1]<br>
2) preceding::staff[2]<br>
3) ../team[1]/staff[1]

Please give me your advice and opinions.

标签: xml xslt xpath
3条回答
淡お忘
2楼-- · 2020-03-26 08:18

It looks to me that you are (also) asking what these axes actually do. As Michael and Har already pointed out, it really depends on the larger picture whether or not to choose one over the other. In practice, the need for preceding, and to a lesser extend preceding-sibling can be a sign of code smell and their performance can be detrimental, depending on input and predicates.

Let's suppose your input is slightly different:

<sales_division>
  <team area="New York">
    <staff>John</staff>
  </team>
  <team area="Tokyo">
    <staff>Sato</staff>
    <staff>Tanaka</staff>
  </team>
  <staff />
  <team area="Osaka">
    <staff>Ueda</staff>
    <staff>Suzuki</staff>
  </team>
</sales_division>

1) preceding-sibling::team/staff[1]

The preceding-sibling axis is a reverse axis, which means it goes from bottom to top through your siblings. The staff step is actually child::staff, which is itself a forward step.

Here you are asking:

  • take all preceding siblings that match team
  • of all these, go down and select the first staff
  • in the augmented XML, this will select "John"

2) preceding::staff[2]

The preceding axis is also a reverse axis, but instead of going over the siblings, it goes over all nodes that have already been passed (meaning: if a node is still in the present ancestor axis, it is not selected), depth first (meaning: text nodes in staff come before staff children, which come before team siblings).

Here you are asking:

  • take all the preceding nodes that match staff, that is empty, Tanaka, Sato and John (in that order).
  • select the second you find
  • in the augmented XML, this will select Tanaka

3) ../team[1]/staff[1]

.. is short for parent::node(). It means take the parent of the current node (which is the root element sales_division)

Here you are asking:

  • go to the parent sales_division
  • from there go down and find the first child matching team
  • from there find the first child matching staff
  • in the augmented XML, this is John

So, in conclusion, it depends on the actual XML structure. Typical use-cases for preceding-sibling are where the order of your XML is known and meaningful (for instance, two coordinates, where the previous sibling is longitude, the current is latitude).

In practice, I see more often that you need an element that is a sibling before or after the current node. In that case, the .. (parent) syntax is probably the best way to go, possibly excluding the current node from the node set. This is easier in XSLT 2.0 and 3.0 than in 1.0.

查看更多
女痞
3楼-- · 2020-03-26 08:18

If you know exactly what is in your XML, you don't need any of these expressions, you could just write "Sato". You presumably need to navigate the XML because you know something about its structure but you don't have complete knowledge of its content. So the way you navigate it is by using what you know in order to discover what you don't know. This makes it impossible for us to help you, because you haven't told us how much of the XML is variable and how much is fixed.

查看更多
爷的心禁止访问
4楼-- · 2020-03-26 08:27

It is better to write a meaningful XPath as it is easier to read and will be easier to maintain. So, I would say I prefer the first way, since it is the closest to the English description of your intention :

preceding-sibling::team/staff[1]

or even more specific (if necessary) :

preceding-sibling::team[@area='Tokyo']/staff[1]

Also, element positions is generally less meaningful and tends to break easily upon slight changes on the XML arrangement, so I'd suggest not to rely on element position index too much.

查看更多
登录 后发表回答