Shortest XPath to find minimum/maximum of sibling dateTime nodes

99封情书 提交于 2019-12-04 19:45:49

You could not in XPath 1.0 if you will not know in advance the number of item because every function wich has a no node-set argument cast its argument taking the first node in node-set, and order comparison operator doesn't work with strings.

In XPath 2.0 you could use:

max(/root/item/d/xs:dateTime(.))

@neo, the XPath expression you list doesn't work when I test it. Try a different data set and you'll see:

<root>
   <item>
      <d>2003-05-30T09:00:00</d>
   </item>
   <item>
      <d>2002-05-30T09:00:00</d>
   </item>
   <item>
      <d>2005-05-30T09:00:00</d>
   </item>
</root>

Your XPath produces 2003-05-30T09:00:00, which obviously is not the max.

And it makes sense that it doesn't work, because the preceding-sibling:: and following-sibling:: axes inside the translate() functions will only yield one sibling each. You're trying to go a general (set) comparison over all the siblings on each axis, but the first argument to translate() has to get converted to a string, before the general comparison operator has a chance to do its thing. Converting a nodeset to a string ignores all nodes except the first one in document order.

Furthermore, translate(./d, 'TZ:-', '.') gives you results like 2003.05.30.09.00.00. That's not a valid number, beyond the '5'. Your test data only works because the years are all different. You would get better results with translate(./d, 'TZ:-', '') which would yield 20030530090000.

Alejandro says it's not possible to do this in XPath 1.0, and he may be right. Let's try it, and maybe we'll learn something even if we don't succeed.

Next, I would try to use the general comparison outside the translate function, so that it can compare whole node-sets. Something like this naive attempt:

/root/item[
    not(following-sibling::item[
         translate($current/d, 'TZ:-', '') &lt;= translate(./d, 'TZ:-', '')])
    and not(preceding-sibling::item[
         translate($current/d, 'TZ:-', '') &lt;= translate(./d, 'TZ:-', '')])]

However this is incomplete as shown by the pseudo-variable $current, which is supposed to refer to the outermost item, the one that is the context node outside all predicates. Unfortunately, XPath 1.0 does not give us a way to refer to that outer context when another context has been pushed on the stack by an inner predicate.

(I seem to recall that \some implementations of XSLT, such as maybe MSXML, allow you to do this using an extended feature like current(1), but I can't find information on that at the moment. Anyway you asked for an XPath solution, and current() is not XPath.)

At this point I'm going to agree with Alejandro that it's impossible in pure, standard XSLT 1.0.

If you specify the environment you're using XPath in, e.g. XSLT, or Javascript, or XQuery, we can probably suggest an efficient way to get what you need. If it's XPath 2.0, Alejandro has your answer.

If you have XQuery 1.0, it should support XPath 2.0, so you can use Alejandro's solution, with doc() to access your input XML document:

max(doc("myInput.xml")/root/item/d/xs:dateTime(.))
Suri2507

It worked for me, but I am looking an enhance xpath now i.e. for below data:

<root>
 <items>
  <item>
   <d>2002-05-30T09:00:00</d>
  </item>
 </items>
 <items>
  <item>
   <d>2005-05-30T09:00:00</d>
  </item>
 </items>
 <items>
  <item>
   <d>2005-05-30T10:00:00</d>
  </item>
 </items>  
</root>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!