james
james

Reputation: 1753

C# same level XML Parsing

I got this XML file:

<Msg UserText="start 0">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 1">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 2">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>

I need to count How many 'A's are between each "start x"

i.e. for the above i would output:

start 0 : 2 
start 1 : 1 
start 2 : 3

How should i go about this in C# ? i had a few directions but I'm sure there are easier ones out there (e.g. using linq)

Upvotes: 0

Views: 260

Answers (4)

Martin Honnen
Martin Honnen

Reputation: 167571

Here is a grouping sample:

XDocument doc = XDocument.Load("../../XMLFile1.xml");

var groups = from msg in doc.Root.Elements("Msg")
             where !((string)msg.Attribute("UserText")).StartsWith("start")
             group msg by 
             msg.ElementsBeforeSelf("Msg").Where(m => 
                 ((string)m.Attribute("UserText")).StartsWith("start")).Last();

foreach (var group in groups)
{
    Console.WriteLine("Group starting with {0} has {1} member(s).", 
        group.Key.Attribute("UserText"), group.Count());
}

With the XML input sample XMLFile1.xml being

<Root>
  <Msg UserText="start 0">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="start 1">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="start 2">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
</Root>

I get the output

Group starting with UserText="start 0" has 2 member(s).
Group starting with UserText="start 1" has 1 member(s).
Group starting with UserText="start 2" has 3 member(s).

Upvotes: 2

Jeff Mercado
Jeff Mercado

Reputation: 134861

Take advantage of the ElementsAfterSelf() method to get the following siblings. Then it's pretty easy from there:

var root = XElement.Parse(xmlStr);
var query =
    from msg in root.Elements("Msg")
    let message = (string)msg.Attribute("UserText")
    where message.StartsWith("start")
    select new
    {
        Message = message,
        FollowingAs = msg.ElementsAfterSelf("Msg")
            .TakeWhile(e => (string)e.Attribute("UserText") == "A")
            .Count(),
    };

Upvotes: 1

George Mamaladze
George Mamaladze

Reputation: 7931

    static void Main(string[] args)
    {
         const string xml = @"<SomeRootTag>
            <Msg UserText='start 0'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='start 1'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='start 2'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
        </SomeRootTag>";

        var xDoc = XDocument.Load(new StringReader(xml));
        var msgs = xDoc.Root.Elements().Where(el => el.Name == "Msg").Select(el => el.Attribute("UserText").Value);
        var results = GetCounts(msgs);



        foreach (var keyValue in results)
        {
            Console.WriteLine("{0}:{1}", keyValue.Item1, keyValue.Item2);
        }

        Console.ReadKey();
    }

    private static IEnumerable<Tuple<string,int>> GetCounts(IEnumerable<string> msgs)
    {
        string last = null;
        int count = 0;
        foreach (var msg in msgs)
        {
            if (msg.StartsWith("start"))
            {
                if (last != null)
                {
                    yield return new Tuple<string, int>(last, count);
                }
                count = 0;
                last = msg;
            }
            else
            {
                count++;
            }
        }
        yield return new Tuple<string, int>(last, count);
    }  

Upvotes: 1

radbyx
radbyx

Reputation: 9660

This should do it for you.

using System.Collections.Generic;
using System.Xml.Linq;

namespace TraverseXMLNodes
{
    public class Class1
    {
        public static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(@"C:\Udvikling\StackOverflow\TraverseXMLNodesSln\TraverseXMLNodes\XMLFile1.xml");
            var msgs = doc.Element("root").Elements("Msg");

            List<int> numbers = new List<int>();
            List<string> numbersStr = new List<string>();
            int count = 0;
            foreach (var xElement in msgs)
            {
                string value = xElement.Attribute("UserText").Value;
                numbersStr.Add(value);
                if (value.Contains("start"))
                {
                    numbers.Add(count);
                    count = 0;
                }
                else
                {
                    count++;
                }
            }
            numbers.Add(count);
        }
    }
}

My XML file:

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <Msg UserText="start 0">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="start 1">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="start 2">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
</root>

Upvotes: 1

Related Questions