tags:

views:

128

answers:

4

How would I find the node(s) with the max version from the following document:

<GateKeeperFiles>
  <File>
    <Name>GateKeeper.exe</Name>
    <Major>2</Major>
    <Minor>1</Minor>
    <Build>1</Build>
    <Revision>6</Revision>
  </File>
  <File>
    <Name>GateKeeper.exe</Name>
    <Major>1</Major>
    <Minor>1</Minor>
    <Build>1</Build>
    <Revision>9</Revision>
  </File>
</GateKeeperFiles>

Ideally this would be acheived with a single XPath function. I currently have two functions which get me the max major value but I can't seem to progress from there.

/GateKeeperFiles/File[not (Major <= preceding-sibling::File/Major) and not(Major <= following-sibling::File/Major)]

or

/GateKeeperFiles/File[not(/GateKeeperFiles/File/Major > Major)]

Cheers, Steve

+1  A: 

If you are using C#, must it be xpath? For example (edited to support multiple files with the same version - post mentioned plural):

        XDocument doc = XDocument.Parse(xml);
        var nodes =
            from file in doc.Document
                .Element("GateKeeperFiles")
                .Elements("File")
            select new {
                   Node = file,
                   Version = new Version(
                     (int) file.Element("Major"),
                     (int) file.Element("Minor"),
                     (int) file.Element("Build"),
                     (int) file.Element("Revision"))
                   } into tmp
                orderby tmp.Version descending
                select tmp;

        var mostRecentVersion = nodes.Select(x => x.Version).FirstOrDefault();
        var files = nodes.TakeWhile(x => x.Version == mostRecentVersion);

        foreach(var file in files) {
            Console.WriteLine("{0}: {1}",
                file.Version,
                (string)file.Node.Element("Name"));
        }


Or with 2.0 (from OP comment):

    static int GetVersion(XmlNode element, string xpath) {
        return int.Parse(element.SelectSingleNode(xpath).InnerText);
    }
    static void Main() {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xml);

        Version bestVersion = null;
        List<XmlElement> files = new List<XmlElement>();
        foreach (XmlElement file in doc.SelectNodes(
                 "/GateKeeperFiles/File")) {
            Version version = new Version(
                GetVersion(file, "Major"), GetVersion(file, "Minor"),
                GetVersion(file, "Build"), GetVersion(file, "Revision"));
            if (bestVersion == null || version > bestVersion) {
                bestVersion = version;
                files.Clear();
                files.Add(file);
            } else if (version == bestVersion) {
                files.Add(file);
            }
        }
        Console.WriteLine("Version: " + bestVersion);
        foreach (XmlElement file in files) {
            Console.WriteLine(file.SelectSingleNode("Name").InnerText);
        }
    }
Marc Gravell
A: 

I have used xsl:sort to achieve similar stuff. Don't remember the exact syntax but try this

After this select the first or the last element. I forgot what is the default behaviour of sort ascending or descending but you can override that with xsl:sort.

Bhushan
The OP doesn't mention xslt, but that could be an option. The default is ascending, btw.
Marc Gravell
A: 

The code should be able to run on .Net 2.0 so the LINQ solution although correct will not suit the scinario.

I'm processing the XML in code and not performing a transform so XSLT would not be suitable either.

Cheers

Edited to include 2.0 version
Marc Gravell
XSLT is also "code" -- adjust your watch! Have you heard about the standard .NET class XslCompiledTransform? Do you know that XSLT is the best option for processing XML because the XSLT language was especially designed for processing XML and tree structured data? Open your eyess, don't be a masohist
Dimitre Novatchev
A: 

Hi Marc,

I have a similar solution to your .Net 2.0 one, I was just hoping for a nice clean XPath version. Looks like this is another area (like bitwise operators) that XPath just doesn't have the answers for.

Cheers

I suspect so, too.
Marc Gravell
This can be done in a single XPath expression, though it will not look pretty and may be quite inefficient if the XPath engine doesn't optimize in a good way.
Dimitre Novatchev