tags:

views:

62

answers:

5

XML Source:

<documents>
    <document>
        <id>3</id>
    </document>
    <document>
        <id>7</id>
    </document>
    <document>
        <id>1</id>
    </document>
</documents>

I need the document-element with the highest value in its id-element (so <document><id>7</id></document> in the example). I can't change the C# code, it is XMLDocument.SelectSingleNode(...), I only can modify the XPath used.

Is there something like documents/document[id=max(id)] or like order by id descending to get it?

+1  A: 

Have a gander at this link. It uses a function math:max().

There is a link for source for that function and explains how it can be implemented

http://www.exslt.org/math/functions/max/index.html

Hope this helps you

jimplode
And after implementing this you can do something like this: /documents/document/id[number(child::text()) < max(number(child::text()))]
Restuta
As far as I see, it is supported by several XSLT processors, but not by .NET ... am I missing something?
Hinek
@Restuts, I need the document node, shouldn't it be more like: /documents/document[number(id/child::text()) = math:max(number(id/child::text()))]
Hinek
A: 

Unfortunately, neither XPath 1.0 or XPath 2.0 define a sort() function. However, most implementations have a sort. For example, <xsl:sort> is available in XSLT implementations, and DOM4J has a sort by XPath method. I'm sure there is something similar in C# but you would have to change the C# code a bit.

Mark Thomas
+1  A: 

You can use the preceding and following XPath axes to check whether there is no greater value:

XmlDocument doc = new XmlDocument();
doc.LoadXml("<documents>"
    + "  <document><id>3</id></document>"
    + "  <document><id>7</id></document>"
    + "  <document><id>1</id></document>"
    + "</documents>");

var max = doc.SelectSingleNode(
    "/documents/document[not(id < preceding::document/id)
                         and not(id < following::document/id)]");

If there are several maximum id values in the document the above code will return the first one. If you want the last element with a maximum id you can use

var max = doc.SelectSingleNode(
    "/documents/document[not(id < preceding::document/id) 
                         and not(id <= following::document/id)]");

If you want to get a list with all elements having a maximum id you can use the SelectNodes method:

var maxList = doc.SelectNodes(
    "/documents/document[not(id < preceding::document/id) 
                         and not(id < following::document/id)]");

XSLT version:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">
      <root>
        <xsl:value-of 
             select="/documents/document/id[not(. &lt;= preceding::document/id) 
                     and not(. &lt;= following::document/id)]"/>
      </root>
    </xsl:template>
</xsl:stylesheet>
0xA3
Would this not just give you the first one it came accross that was higher than the one before and the one after it?
jimplode
sorry, my mistake.
jimplode
+4  A: 
documents/document[not(../document/id > id)]
Nick Jones
@Nick Jones: +1 For shortest and correct XPath 1.0 maximum expression, besides cuadratic complexity...
Alejandro
thanks, great solution, you should get bonus points for this one!
Hinek
Clever solution!
Mark Thomas
+1  A: 

Besides Nick Jones XPath 1.0 correct answer, in XPath 2.0:

/documents/document[id = max(../document/id)]
Alejandro