14

I have a plist file from an iPhone app. It looks like this below:

<plist version="1.0">
  <dict>
    <key>barcodes</key>
    <array>
      <string>JF893J89FJ-66666</string>
      <string>JF893J89FJ-55555</string>
    </array>
    <key>currentStep</key>
    <integer>1</integer>
    <key>dateFinished</key>
    <date>2010-05-10T18:33:25Z</date>
    <key>dateStarted</key>
    <date>2010-05-10T18:33:25Z</date>
    <key>description</key>
    <string>TEST</string>
    <key>geoRequired</key>
    <string>N</string>
    <key>inProgress</key>
    <string>N</string>
    <key>jobID</key>
    <integer>10085</integer>
    <key>jobSteps</key>
    <array>
      <dict>
        <key>label</key>
        <string>TEST</string>
        <key>response</key>
        <string>matt hudson</string>
        <key>stepID</key>
        <integer>1103</integer>
        <key>typeID</key>
        <integer>4</integer>
      </dict>
    </array>
  </dict>
</plist>

I need to get the array after jobSteps.

I have this so far:

XDocument xml = XDocument.Load(rri.Response);

var q = from elements in xml.Descendants("plist").Descendants("dict")
        where elements.Value == "jobSteps"
        select elements;

But I need to get the next item after the element that has jobSteps in it.

decyclone
  • 30,394
  • 6
  • 63
  • 80
Matt Hudson
  • 7,329
  • 5
  • 49
  • 66

4 Answers4

18

It's not entirely clear to me whether Adam's solution is what you want, but if not, you might want to look at the NextNode property:

Gets the next sibling node of this node.

For instance, this prints the array element:

using System;
using System.Linq;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        XDocument doc = XDocument.Load("test.xml");
        foreach (var element in doc.Descendants("key")
                                   .Where(x => (string) x == "jobSteps"))
        {
            Console.WriteLine(element.NextNode);
        }
    }
}

Note that it's skipping the whitespace between elements - but if there were any text between that and the array, it wouldn't work - so you'd want:

Console.WriteLine(element.NodesAfterSelf().OfType<XElement>().First());
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • There are a lot of great answers, but I did use NextNode and that's what I was looking for. – Matt Hudson Feb 25 '11 at 15:31
  • Thanks - it became clear my original answer wasn't sufficient shortly before you posted. +1 :) – Adam Rackis Feb 25 '11 at 15:34
  • Jon, for my edification, when you say CW(element.NextNode) - does that display the XML Value of the node? I know XElement : XNode, I'm just wondering what use it is working with a Node vis-a-vis an Element. – Adam Rackis Feb 25 '11 at 15:37
  • @Adam: It displays whatever the node's `ToString` method returns. For a text node, it's the text... for an element it's the complete element including descendants. Usually it *is* easier to work with XElement than XNode, yes. – Jon Skeet Feb 25 '11 at 15:40
5

EDIT

I believe this will get you the element after the jobSteps node:

XElement elementAfterJobsteps = xml.Descendants("plist").FirstOrDefault().Descendants("jobSteps").FirstOrDefault().ElementsAfterSelf().FirstOrDefault();

END EDIT

foreach (XElement El in xml.Descendants("plist")) {
        var localResults = 
            from elements in El.Descendants("dict")
            where elements.Value == "jobSteps"
            select elements;

       //process localResults 
 }

Or, even simpler, try method syntax

 foreach (XElement El in xml.Descendants("plist")) {
    var localResults = El.Descendants("dict").Where(dict => dict.Value == "jobSteps");

    //process localResults 
 }
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
2
var q = xml
        .Descendants("plist")
        .Descendants("dict")
        .Where(item => item.Value == "jobSteps")
        .Select(item => item.NextNode)
        .SingleOrDefault() // add this if you expect single match
        ;

The q will be either a single array node or a sequence of array nodes depending whether you use SingleOrDefault().

mgronber
  • 3,399
  • 16
  • 20
  • This was really good too! I ended up going with this: XNode node = xmlElements.Elements("dict").Elements("key").Where(k => k.Value == "jobSteps").FirstOrDefault(); – Matt Hudson Feb 25 '11 at 15:32
1
var q = (from key in xml.Descendants("key")
            where key.Value == "jobSteps"
            from array in xml.Descendants("array")
            select key.NodesAfterSelf() // In all nodes after <key>jobSteps</key>
                    .OfType<XElement>() // which are elements
                    .Where(element => element.Name == "array") // and name is array,
                    .First() // select first of them
        ).First(); // and select first of whatever is found

NOTE: Above code may throw InvalidOperationException while calling First() if no result is found.

decyclone
  • 30,394
  • 6
  • 63
  • 80